Merge branch 'innolab' of https://github.com/ciderapp/Cider into innolab
This commit is contained in:
commit
ecfdeece64
22 changed files with 566 additions and 128 deletions
|
@ -121,4 +121,10 @@ Update 16/02/2022 21:45 UTC
|
||||||
|
|
||||||
* `term.audioControls`: Added for `en_US`.
|
* `term.audioControls`: Added for `en_US`.
|
||||||
* `settings.option.audio.volumeStep`: Added for `en_US`.
|
* `settings.option.audio.volumeStep`: Added for `en_US`.
|
||||||
* `settings.option.audio.maxVolume`: Added for `en_US`.`
|
* `settings.option.audio.maxVolume`: Added for `en_US`.`
|
||||||
|
|
||||||
|
Update 17/02/2022 10:00 UTC
|
||||||
|
+ `settings.header.debug`: Added for `en_US`.
|
||||||
|
+ `settings.option.debug.copy_log`: Replaces `settings.option.experimental.copy_log`.
|
||||||
|
+ `settings.option.debug.openAppData`: Added for `en_US`
|
||||||
|
+ `action.open`: Added for `en_US`
|
|
@ -231,7 +231,7 @@
|
||||||
"action.copy": "Copy",
|
"action.copy": "Copy",
|
||||||
"action.newpreset": "New Preset...", // Equalizer Preset
|
"action.newpreset": "New Preset...", // Equalizer Preset
|
||||||
"action.deletepreset": "Delete Preset",
|
"action.deletepreset": "Delete Preset",
|
||||||
|
"action.open": "Open",
|
||||||
// Settings - General
|
// Settings - General
|
||||||
"settings.header.general": "General",
|
"settings.header.general": "General",
|
||||||
"settings.header.general.description": "Adjust the general settings for Cider.",
|
"settings.header.general.description": "Adjust the general settings for Cider.",
|
||||||
|
@ -339,13 +339,19 @@
|
||||||
"settings.option.connectivity.lastfmScrobble.removeFeatured": "Remove featuring artists from song title (Last.fm)",
|
"settings.option.connectivity.lastfmScrobble.removeFeatured": "Remove featuring artists from song title (Last.fm)",
|
||||||
"settings.option.connectivity.lastfmScrobble.filterLoop": "Filter looped track (Last.fm)",
|
"settings.option.connectivity.lastfmScrobble.filterLoop": "Filter looped track (Last.fm)",
|
||||||
// Refer to term.connect for the connect button
|
// Refer to term.connect for the connect button
|
||||||
|
|
||||||
|
// Settings - Debug
|
||||||
|
"settings.header.debug": "Debug",
|
||||||
|
"settings.option.debug.copy_log": "Copy logs to clipboard",
|
||||||
|
"settings.option.debug.openAppData": "Open Cider Folder",
|
||||||
|
|
||||||
|
|
||||||
// Settings - Experimental
|
// Settings - Experimental
|
||||||
"settings.header.experimental": "Experimental",
|
"settings.header.experimental": "Experimental",
|
||||||
"settings.header.experimental.description": "Adjust the experimental settings for Cider.",
|
"settings.header.experimental.description": "Adjust the experimental settings for Cider.",
|
||||||
"settings.option.experimental.compactUI": "Compact UI", // Toggle
|
"settings.option.experimental.compactUI": "Compact UI", // Toggle
|
||||||
"settings.option.experimental.close_button_hide": "Close Button Should Hide the Application",
|
"settings.option.experimental.close_button_hide": "Close Button Should Hide the Application",
|
||||||
"settings.option.experimental.copy_log": "Copy logs to clipboard",
|
|
||||||
"settings.option.experimental.inline_playlists": "Inline Playlists and Albums",
|
"settings.option.experimental.inline_playlists": "Inline Playlists and Albums",
|
||||||
|
|
||||||
// Refer to term.disabled & term.enabled
|
// Refer to term.disabled & term.enabled
|
||||||
|
|
|
@ -215,6 +215,8 @@
|
||||||
// Settings - Audio
|
// Settings - Audio
|
||||||
"settings.header.audio": "オーディオ",
|
"settings.header.audio": "オーディオ",
|
||||||
"settings.header.audio.description": "Ciderのオーディオ設定",
|
"settings.header.audio.description": "Ciderのオーディオ設定",
|
||||||
|
"settings.option.audio.volumeStep": "音量調整のステップ",
|
||||||
|
"settings.option.audio.maxVolume": "最大音量",
|
||||||
"settings.option.audio.quality": "音質", // Dropdown
|
"settings.option.audio.quality": "音質", // Dropdown
|
||||||
"settings.header.audio.quality.hireslossless": "ハイレゾロスレス",
|
"settings.header.audio.quality.hireslossless": "ハイレゾロスレス",
|
||||||
"settings.header.audio.quality.hireslossless.description": "(最大解像度 24 ビット/192 kHz)",
|
"settings.header.audio.quality.hireslossless.description": "(最大解像度 24 ビット/192 kHz)",
|
||||||
|
|
|
@ -229,6 +229,8 @@
|
||||||
// Settings - Audio
|
// Settings - Audio
|
||||||
"settings.header.audio": "音訊",
|
"settings.header.audio": "音訊",
|
||||||
"settings.header.audio.description": "調整 Cider 的音訊設定",
|
"settings.header.audio.description": "調整 Cider 的音訊設定",
|
||||||
|
"settings.option.audio.volumeStep": "音量改變量",
|
||||||
|
"settings.option.audio.maxVolume": "最高音量",
|
||||||
"settings.option.audio.quality": "音訊品質", // Dropdown
|
"settings.option.audio.quality": "音訊品質", // Dropdown
|
||||||
"settings.header.audio.quality.hireslossless": "高品質無損壓縮",
|
"settings.header.audio.quality.hireslossless": "高品質無損壓縮",
|
||||||
"settings.header.audio.quality.hireslossless.description": "(高達 24-bit/192 kHz)",
|
"settings.header.audio.quality.hireslossless.description": "(高達 24-bit/192 kHz)",
|
||||||
|
|
|
@ -47,6 +47,7 @@ export class BrowserWindow {
|
||||||
"pages/library-videos",
|
"pages/library-videos",
|
||||||
"pages/remote-pair",
|
"pages/remote-pair",
|
||||||
"pages/themes-github",
|
"pages/themes-github",
|
||||||
|
"pages/replay",
|
||||||
"components/mediaitem-artwork",
|
"components/mediaitem-artwork",
|
||||||
"components/artwork-material",
|
"components/artwork-material",
|
||||||
"components/menu-panel",
|
"components/menu-panel",
|
||||||
|
@ -125,6 +126,16 @@ export class BrowserWindow {
|
||||||
this.options.width = windowState.width;
|
this.options.width = windowState.width;
|
||||||
this.options.height = windowState.height;
|
this.options.height = windowState.height;
|
||||||
|
|
||||||
|
switch(process.platform) {
|
||||||
|
default:
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "linux":
|
||||||
|
this.options.autoHideMenuBar = true
|
||||||
|
this.options.frame = true
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Start the webserver for the browser window to load
|
// Start the webserver for the browser window to load
|
||||||
|
|
||||||
this.startWebServer();
|
this.startWebServer();
|
||||||
|
@ -699,7 +710,9 @@ export class BrowserWindow {
|
||||||
ipcMain.on('get-version', (_event) => {
|
ipcMain.on('get-version', (_event) => {
|
||||||
_event.returnValue = app.getVersion()
|
_event.returnValue = app.getVersion()
|
||||||
});
|
});
|
||||||
|
ipcMain.on('open-appdata', (_event) => {
|
||||||
|
shell.openPath(app.getPath('userData'));
|
||||||
|
});
|
||||||
/* *********************************************************************************************
|
/* *********************************************************************************************
|
||||||
* Window Events
|
* Window Events
|
||||||
* **********************************************************************************************/
|
* **********************************************************************************************/
|
||||||
|
|
|
@ -27,7 +27,7 @@ export class Store {
|
||||||
},
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"volume": 1,
|
"volume": 1,
|
||||||
"volumeStep": 0.1,
|
"volumeStep": 0.02,
|
||||||
"maxVolume": 1,
|
"maxVolume": 1,
|
||||||
"lastVolume": 1,
|
"lastVolume": 1,
|
||||||
"muted": false,
|
"muted": false,
|
||||||
|
|
|
@ -69,9 +69,9 @@ ipcMain.on('nowPlayingItemDidChange', (_event, attributes) => {
|
||||||
CiderPlug.callPlugins('onNowPlayingItemDidChange', attributes);
|
CiderPlug.callPlugins('onNowPlayingItemDidChange', attributes);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('lfmItemChange', (_event, attributes) => {
|
ipcMain.on('nowPlayingItemDidChangeLastFM', (_event, attributes) => {
|
||||||
CiderPlug.callPlugins('lfmItemChange', attributes);
|
CiderPlug.callPlugin('lastfm', 'nowPlayingItemDidChangeLastFM', attributes);
|
||||||
});
|
})
|
||||||
|
|
||||||
app.on('before-quit', () => {
|
app.on('before-quit', () => {
|
||||||
CiderPlug.callPlugins('onBeforeQuit');
|
CiderPlug.callPlugins('onBeforeQuit');
|
||||||
|
|
|
@ -79,46 +79,47 @@ export default class LastFMPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
private scrobbleSong(attributes: any) {
|
private scrobbleSong(attributes: any) {
|
||||||
if(this._timer) clearTimeout(this._timer);
|
if (this._timer) clearTimeout(this._timer);
|
||||||
var self = this;
|
var self = this;
|
||||||
this._timer = setTimeout(async () => {
|
this._timer = setTimeout(async () => {
|
||||||
const currentAttributes = attributes;
|
const currentAttributes = attributes;
|
||||||
|
|
||||||
if (!self._lastfm || self._lastfm.cachedAttributes === attributes) {
|
if (!self._lastfm || self._lastfm.cachedAttributes === attributes) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self._lastfm.cachedAttributes) {
|
if (self._lastfm.cachedAttributes) {
|
||||||
if (self._lastfm.cachedAttributes.playParams.id === attributes.playParams.id) return;
|
if (self._lastfm.cachedAttributes.playParams.id === attributes.playParams.id) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const artist = await this.getPrimaryArtist(attributes)
|
const artist = await this.getPrimaryArtist(attributes)
|
||||||
|
|
||||||
if (currentAttributes.status && currentAttributes === attributes) {
|
if (currentAttributes.status && currentAttributes === attributes) {
|
||||||
if (fs.existsSync(this.sessionPath)) {
|
if (fs.existsSync(this.sessionPath)) {
|
||||||
// Scrobble playing song.
|
// Scrobble playing song.
|
||||||
if (attributes.status === true) {
|
if (attributes.status === true) {
|
||||||
self._lastfm.track.scrobble({
|
self._lastfm.track.scrobble({
|
||||||
'artist': artist,
|
'artist': artist,
|
||||||
'track': attributes.name,
|
'track': attributes.name,
|
||||||
'album': attributes.albumName,
|
'album': attributes.albumName,
|
||||||
'albumArtist': artist,
|
'albumArtist': artist,
|
||||||
'timestamp': new Date().getTime() / 1000
|
'timestamp': new Date().getTime() / 1000
|
||||||
}, function (err: any, scrobbled: any) {
|
}, function (err: any, scrobbled: any) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return console.error('[LastFM] An error occurred while scrobbling', err);
|
return console.error('[LastFM] An error occurred while scrobbling', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[LastFM] Successfully scrobbled: ', scrobbled);
|
console.log('[LastFM] Successfully scrobbled: ', scrobbled);
|
||||||
});
|
});
|
||||||
self._lastfm.cachedAttributes = attributes
|
self._lastfm.cachedAttributes = attributes
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.authenticate();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.authenticate();
|
return console.log('[LastFM] Did not add ', attributes.name, '—', artist, 'because now playing a other song.');
|
||||||
}
|
}
|
||||||
} else {
|
}, Math.round(attributes.durationInMillis * (self._store.lastfm.scrobble_after / 100)));
|
||||||
return console.log('[LastFM] Did not add ', attributes.name, '—', artist, 'because now playing a other song.');
|
|
||||||
}},Math.round(attributes.durationInMillis * (self._store.lastfm.scrobble_after / 100)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateNowPlayingSong(attributes: any) {
|
private async updateNowPlayingSong(attributes: any) {
|
||||||
|
@ -155,7 +156,7 @@ export default class LastFMPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPrimaryArtist (attributes: any) {
|
private async getPrimaryArtist(attributes: any) {
|
||||||
const songId = attributes.playParams.catalogId || attributes.playParams.id
|
const songId = attributes.playParams.catalogId || attributes.playParams.id
|
||||||
|
|
||||||
if (!this._store.lastfm.enabledRemoveFeaturingArtists || !songId) return attributes.artistName;
|
if (!this._store.lastfm.enabledRemoveFeaturingArtists || !songId) return attributes.artistName;
|
||||||
|
@ -257,22 +258,22 @@ export default class LastFMPlugin {
|
||||||
* @param attributes Music Attributes (attributes.status = current state)
|
* @param attributes Music Attributes (attributes.status = current state)
|
||||||
*/
|
*/
|
||||||
onPlaybackStateDidChange(attributes: object): void {
|
onPlaybackStateDidChange(attributes: object): void {
|
||||||
this.updateNowPlayingSong(attributes)
|
this.updateNowPlayingSong(attributes)
|
||||||
// this.scrobbleSong(attributes)
|
// this.scrobbleSong(attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs on song change
|
* Runs on song change
|
||||||
* @param attributes Music Attributes
|
* @param attributes Music Attributes
|
||||||
*/
|
*/
|
||||||
lfmItemChange(attributes: any): void {
|
nowPlayingItemDidChangeLastFM(attributes: any): void {
|
||||||
attributes.status = true
|
attributes.status = true
|
||||||
if (!this._store.lastfm.filterLoop){
|
if (!this._store.lastfm.filterLoop) {
|
||||||
this._lastfm.cachedNowPlayingAttributes = false;
|
this._lastfm.cachedNowPlayingAttributes = false;
|
||||||
this._lastfm.cachedAttributes = false
|
this._lastfm.cachedAttributes = false
|
||||||
}
|
}
|
||||||
this.updateNowPlayingSong(attributes)
|
this.updateNowPlayingSong(attributes)
|
||||||
this.scrobbleSong(attributes)
|
this.scrobbleSong(attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@ let cache = {playParams: {id: 0}, status: null, remainingTime: 0},
|
||||||
const MusicKitInterop = {
|
const MusicKitInterop = {
|
||||||
init: function () {
|
init: function () {
|
||||||
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => {
|
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => {
|
||||||
if (MusicKitInterop.filterTrack(MusicKitInterop.getAttributes(), true, false)) {
|
const attributes = MusicKitInterop.getAttributes()
|
||||||
global.ipcRenderer.send('playbackStateDidChange', MusicKitInterop.getAttributes())
|
if (MusicKitInterop.filterTrack(attributes, true, false)) {
|
||||||
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
|
global.ipcRenderer.send('playbackStateDidChange', attributes)
|
||||||
|
ipcRenderer.send('wsapi-updatePlaybackState', attributes);
|
||||||
// if (typeof _plugins != "undefined") {
|
// if (typeof _plugins != "undefined") {
|
||||||
// _plugins.execute("OnPlaybackStateChanged", {Attributes: MusicKitInterop.getAttributes()})
|
// _plugins.execute("OnPlaybackStateChanged", {Attributes: MusicKitInterop.getAttributes()})
|
||||||
// }
|
// }
|
||||||
|
@ -23,12 +24,18 @@ const MusicKitInterop = {
|
||||||
/** wsapi */
|
/** wsapi */
|
||||||
|
|
||||||
MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, async () => {
|
MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, async () => {
|
||||||
// await MusicKitInterop.modifyNamesOnLocale();
|
const attributes = MusicKitInterop.getAttributes()
|
||||||
if (MusicKitInterop.filterTrack(MusicKitInterop.getAttributes(), false, true) || !app.cfg.lastfm.filterLoop) {
|
const trackFilter = MusicKitInterop.filterTrack(attributes, false, true)
|
||||||
global.ipcRenderer.send('lfmItemChange', MusicKitInterop.getAttributes());
|
|
||||||
|
if (trackFilter) {
|
||||||
|
global.ipcRenderer.send('nowPlayingItemDidChange', attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.ipcRenderer.send('nowPlayingItemDidChange', MusicKitInterop.getAttributes());
|
// LastFM's Custom Call
|
||||||
|
await MusicKitInterop.modifyNamesOnLocale();
|
||||||
|
if (trackFilter || !app.cfg.lastfm.filterLoop) {
|
||||||
|
global.ipcRenderer.send('nowPlayingItemDidChangeLastFM', attributes);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
MusicKit.getInstance().addEventListener(MusicKit.Events.authorizationStatusDidChange, () => {
|
MusicKit.getInstance().addEventListener(MusicKit.Events.authorizationStatusDidChange, () => {
|
||||||
|
@ -69,7 +76,7 @@ const MusicKitInterop = {
|
||||||
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
|
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
|
||||||
|
|
||||||
attributes.status = isPlayingExport ?? false;
|
attributes.status = isPlayingExport ?? false;
|
||||||
attributes.name = attributes?.name ?? 'No Title Found';
|
attributes.name = attributes?.name ?? 'no-title-found';
|
||||||
attributes.artwork = attributes?.artwork ?? {url: ''};
|
attributes.artwork = attributes?.artwork ?? {url: ''};
|
||||||
attributes.artwork.url = (attributes?.artwork?.url ?? '').replace(`{f}`, "png");
|
attributes.artwork.url = (attributes?.artwork?.url ?? '').replace(`{f}`, "png");
|
||||||
attributes.playParams = attributes?.playParams ?? {id: 'no-id-found'};
|
attributes.playParams = attributes?.playParams ?? {id: 'no-id-found'};
|
||||||
|
@ -98,7 +105,7 @@ const MusicKitInterop = {
|
||||||
},
|
},
|
||||||
|
|
||||||
filterTrack: function (a, playbackCheck, mediaCheck) {
|
filterTrack: function (a, playbackCheck, mediaCheck) {
|
||||||
if (a.title === "No Title Found" || a.playParams.id === "no-id-found") {
|
if (a.name === 'no-title-found' || a.playParams.id === "no-id-found") {
|
||||||
return;
|
return;
|
||||||
} else if (mediaCheck && a.playParams.id === cache.playParams.id) {
|
} else if (mediaCheck && a.playParams.id === cache.playParams.id) {
|
||||||
return;
|
return;
|
||||||
|
|
1
src/renderer/assets/feather/external-link.svg
Normal file
1
src/renderer/assets/feather/external-link.svg
Normal 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-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>
|
After Width: | Height: | Size: 388 B |
|
@ -152,13 +152,12 @@ var CiderAudio = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
llpw_h2_2: function (status, hierarchy){
|
llpw_h2_2: function (status, hierarchy){
|
||||||
if (status === true) { // 23 Band Adjustment
|
if (status === true) {
|
||||||
let LLPW_Q = [5, 1, 3.536, 1.25, 8.409, 1.25, 14.14, 7.071, 5, 0.625, 16.82, 20, 20, 20, 28.28, 28.28, 28.28, 20, 33.64, 33.64, 10, 28.28, 7.071, 3.856];
|
let LLPW_Q = [5, 1, 3.536, 1.25, 8.409, 1.25, 14.14, 7.071, 5, 0.625, 16.82, 20, 20, 20, 28.28, 28.28, 28.28, 20, 33.64, 33.64, 10, 28.28, 7.071, 3.856];
|
||||||
let LLPW_GAIN = [0.38, -1.81, -0.23, -0.51, 0.4, 0.84, 0.36, -0.34, 0.27, -1.2, -0.42, -0.67, 0.81, 1.31, -0.71, 0.68, -1.04, 0.79, -0.73, -1.33, 1.17, 0.57, 0.35, 6.33];
|
let LLPW_GAIN = [0.38, -1.81, -0.23, -0.51, 0.4, 0.84, 0.36, -0.34, 0.27, -1.2, -0.42, -0.67, 0.81, 1.31, -0.71, 0.68, -1.04, 0.79, -0.73, -1.33, 1.17, 0.57, 0.35, 6.33];
|
||||||
let LLPW_FREQUENCIES = [16.452, 24.636, 37.134, 74.483, 159.54, 308.18, 670.21, 915.81, 1200.7, 2766.4, 2930.6, 4050.6, 4409.1, 5395.2, 5901.6, 6455.5, 7164.1, 7724.1, 8449, 10573, 12368, 14198, 17910, 18916];
|
let LLPW_FREQUENCIES = [16.452, 24.636, 37.134, 74.483, 159.54, 308.18, 670.21, 915.81, 1200.7, 2766.4, 2930.6, 4050.6, 4409.1, 5395.2, 5901.6, 6455.5, 7164.1, 7724.1, 8449, 10573, 12368, 14198, 17910, 18916];
|
||||||
CiderAudio.audioNodes.llpw = []
|
CiderAudio.audioNodes.llpw = []
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < LLPW_FREQUENCIES.length; i++) {
|
for (i = 0; i < LLPW_FREQUENCIES.length; i++) {
|
||||||
CiderAudio.audioNodes.llpw[i] = CiderAudio.context.createBiquadFilter();
|
CiderAudio.audioNodes.llpw[i] = CiderAudio.context.createBiquadFilter();
|
||||||
CiderAudio.audioNodes.llpw[i].type = 'peaking'; // 'peaking';
|
CiderAudio.audioNodes.llpw[i].type = 'peaking'; // 'peaking';
|
||||||
|
|
|
@ -1354,8 +1354,15 @@ const app = new Vue({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getNowPlayingItemDetailed(target) {
|
async getNowPlayingItemDetailed(target) {
|
||||||
let u = await app.mkapi(app.mk.nowPlayingItem.playParams.kind, (app.mk.nowPlayingItem.songId == -1), (app.mk.nowPlayingItem.songId != -1) ? app.mk.nowPlayingItem.songId : app.mk.nowPlayingItem["id"], {"include[songs]": "albums,artists", l : app.mklang});
|
try {
|
||||||
app.searchAndNavigate(u.data.data[0], target)
|
let u = await app.mkapi(app.mk.nowPlayingItem.playParams.kind,
|
||||||
|
(app.mk.nowPlayingItem.songId == -1),
|
||||||
|
(app.mk.nowPlayingItem.songId != -1) ? app.mk.nowPlayingItem.songId : app.mk.nowPlayingItem["id"],
|
||||||
|
{ "include[songs]": "albums,artists", l: app.mklang });
|
||||||
|
app.searchAndNavigate(u.data.data[0], target)
|
||||||
|
} catch (e) {
|
||||||
|
app.searchAndNavigate(app.mk.nowPlayingItem, target)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async searchAndNavigate(item, target) {
|
async searchAndNavigate(item, target) {
|
||||||
let self = this
|
let self = this
|
||||||
|
@ -3037,7 +3044,7 @@ const app = new Vue({
|
||||||
type += "s"
|
type += "s"
|
||||||
}
|
}
|
||||||
type = type.replace("library-", "")
|
type = type.replace("library-", "")
|
||||||
let id = item.attributes.playParams.catalogId ?? item.id
|
let id = item.attributes.playParams.catalogId ?? item.attributes.playParams.id ?? item.id
|
||||||
|
|
||||||
let index = types.findIndex(function (type) {
|
let index = types.findIndex(function (type) {
|
||||||
return type.type == this
|
return type.type == this
|
||||||
|
|
6
src/renderer/less/linux.less
Normal file
6
src/renderer/less/linux.less
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Linux
|
||||||
|
body[platform="linux"] {
|
||||||
|
#window-controls-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -819,3 +819,180 @@
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Home
|
||||||
|
.home-page {
|
||||||
|
top: 0;
|
||||||
|
padding-top: var(--navbarHeight);
|
||||||
|
|
||||||
|
.md-btn-replay {
|
||||||
|
background: var(--replayGradient);
|
||||||
|
border: 0px;
|
||||||
|
box-shadow: inset 0px 0px 0px 1px rgba(200, 200, 200, 0.2);
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.md-btn-replay--hero {
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--replayGradient);
|
||||||
|
border: 0px;
|
||||||
|
box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 20%);
|
||||||
|
margin-top: 1em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artist-feed-card {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 10%;
|
||||||
|
z-index: 1;
|
||||||
|
background: black;
|
||||||
|
width: 80%;
|
||||||
|
height: 96%;
|
||||||
|
overflow: scroll;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col.madeforyou-col {
|
||||||
|
width: 420px;
|
||||||
|
min-width: 0px;
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.well.artistfeed-well {
|
||||||
|
margin-top: 0px;
|
||||||
|
height: 392px;
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-text {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: rgb(200 200 200 / 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-icon {
|
||||||
|
border-radius: 100%;
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: var(--mediaItemShadow-Shadow);
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.well.profile-well {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
margin: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handle {
|
||||||
|
margin: 4px;
|
||||||
|
opacity: 0.7;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replay
|
||||||
|
.replay-page {
|
||||||
|
--replayTextShadow: 0px 3px 2px #6f3f52;
|
||||||
|
|
||||||
|
.replay-period {
|
||||||
|
height: 200px;
|
||||||
|
width: 200px;
|
||||||
|
margin: 6px;
|
||||||
|
border-radius: var(--mediaItemRadius);
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform .2s var(--appleEase);
|
||||||
|
transition-delay: .1s;
|
||||||
|
align-self: center;
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-6px);
|
||||||
|
transition-delay: 0s;
|
||||||
|
}
|
||||||
|
.artwork-container {
|
||||||
|
height:200px;
|
||||||
|
width:200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-genres-container {
|
||||||
|
|
||||||
|
.genre-name {
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin: 6px 0px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.genre-count {
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
background: #ffffff14;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.genre-count-bar {
|
||||||
|
height: 100%;
|
||||||
|
width: 0%;
|
||||||
|
background: var(--keyColor);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 32px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-mediaitem-square {
|
||||||
|
.mediaitem-artwork {
|
||||||
|
animation: replayFadeIn .5s var(--appleEase);
|
||||||
|
}
|
||||||
|
transition: transform .2s var(--appleEase);
|
||||||
|
transition-delay: .1s;
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
transition-delay: 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes replayFadeIn {
|
||||||
|
0% {
|
||||||
|
//border-radius: 100%;
|
||||||
|
transform: translateY(10px) scale(0.9);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
//border-radius: var(--mediaItemRadius);
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.replay-viewport {
|
||||||
|
background: var(--replayGradient);
|
||||||
|
padding: 16px 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: var(--mediaItemShadow), var(--mediaItemShadow-Shadow);
|
||||||
|
color: rgb(238 238 238 / 86%);
|
||||||
|
|
||||||
|
.replay-header {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 3em;
|
||||||
|
text-shadow: var(--replayTextShadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.replay-card {
|
||||||
|
background: transparent;
|
||||||
|
border:0px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,7 @@
|
||||||
--songProgressColor: var(--keyColor);
|
--songProgressColor: var(--keyColor);
|
||||||
--songProgressBackground: #333;
|
--songProgressBackground: #333;
|
||||||
--textColor: #eee;
|
--textColor: #eee;
|
||||||
|
--replayGradient: linear-gradient(45deg, hsl(248deg 58% 29%), hsl(13deg 41% 42%));
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
|
@ -1085,6 +1086,7 @@ body[platform="darwin"] .app-chrome .app-chrome-item > .window-controls > div.cl
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
border-left: 1px solid rgb(200 200 200 / 8%);
|
border-left: 1px solid rgb(200 200 200 / 8%);
|
||||||
border-right: 1px solid rgb(200 200 200 / 8%);
|
border-right: 1px solid rgb(200 200 200 / 8%);
|
||||||
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-chrome .app-chrome-item > .app-playback-controls {
|
.app-chrome .app-chrome-item > .app-playback-controls {
|
||||||
|
@ -1092,6 +1094,7 @@ body[platform="darwin"] .app-chrome .app-chrome-item > .window-controls > div.cl
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-chrome .app-chrome-item > .app-playback-controls {
|
.app-chrome .app-chrome-item > .app-playback-controls {
|
||||||
|
@ -1220,6 +1223,7 @@ body[platform="darwin"] .app-chrome .app-chrome-item > .window-controls > div.cl
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background-color: rgb(200 200 200 / 10%);
|
background-color: rgb(200 200 200 / 10%);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
&::-webkit-slider-thumb {
|
&::-webkit-slider-thumb {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -1645,66 +1649,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-page {
|
|
||||||
top: 0;
|
|
||||||
padding-top: var(--navbarHeight);
|
|
||||||
|
|
||||||
.artist-feed-card {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 10%;
|
|
||||||
z-index: 1;
|
|
||||||
background: black;
|
|
||||||
width: 80%;
|
|
||||||
height: 96%;
|
|
||||||
overflow: scroll;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col.madeforyou-col {
|
|
||||||
width: 420px;
|
|
||||||
min-width: 0px;
|
|
||||||
max-width: 420px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well.artistfeed-well {
|
|
||||||
margin-top: 0px;
|
|
||||||
height: 392px;
|
|
||||||
align-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint-text {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: rgb(200 200 200 / 70%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-icon {
|
|
||||||
border-radius: 100%;
|
|
||||||
width: 128px;
|
|
||||||
height: 128px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: var(--mediaItemShadow-Shadow);
|
|
||||||
margin: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well.profile-well {
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.name {
|
|
||||||
margin: 4px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.handle {
|
|
||||||
margin: 4px;
|
|
||||||
opacity: 0.7;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cider */
|
/* Cider */
|
||||||
|
|
||||||
.more-btn-round {
|
.more-btn-round {
|
||||||
|
@ -2756,6 +2700,17 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
|
|
||||||
/* Transitions */
|
/* Transitions */
|
||||||
|
|
||||||
|
.replaycard-enter-active,
|
||||||
|
.replaycard-leave-active {
|
||||||
|
transition: opacity .5s var(--appleEase), transform .5s var(--appleEase);
|
||||||
|
}
|
||||||
|
|
||||||
|
.replaycard-enter,
|
||||||
|
.replaycard-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
|
||||||
.modal-enter-active,
|
.modal-enter-active,
|
||||||
.modal-leave-active {
|
.modal-leave-active {
|
||||||
transition: opacity .1s var(--appleEase), transform .1s var(--appleEase);
|
transition: opacity .1s var(--appleEase), transform .1s var(--appleEase);
|
||||||
|
@ -3155,4 +3110,5 @@ body[platform='darwin'] {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@import url("less/linux.less");
|
||||||
@import url("less/compact.less");
|
@import url("less/compact.less");
|
||||||
|
|
|
@ -172,4 +172,11 @@
|
||||||
</template>
|
</template>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
|
<!-- Replay -->
|
||||||
|
<transition name="wpfade">
|
||||||
|
<template v-if="page == 'replay'">
|
||||||
|
<replay-page></replay-page>
|
||||||
|
</template>
|
||||||
|
</transition>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -3,7 +3,7 @@
|
||||||
<vue-horizontal>
|
<vue-horizontal>
|
||||||
<div v-for="items in itemPages">
|
<div v-for="items in itemPages">
|
||||||
<mediaitem-list-item
|
<mediaitem-list-item
|
||||||
v-for="(song, index) in items" :parent="'listitem-hr' + simplifiedParent"
|
v-for="(song, index) in items" :show-library-status="showLibraryStatus" :parent="'listitem-hr' + simplifiedParent"
|
||||||
:index="song.index"
|
:index="song.index"
|
||||||
:item="song"></mediaitem-list-item>
|
:item="song"></mediaitem-list-item>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,6 +19,10 @@
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
showLibraryStatus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
|
|
|
@ -74,6 +74,9 @@
|
||||||
<div class="duration" v-if="displayDuration" @dblclick="route()">
|
<div class="duration" v-if="displayDuration" @dblclick="route()">
|
||||||
{{ msToMinSec(item.attributes.durationInMillis ?? 0) }}
|
{{ msToMinSec(item.attributes.durationInMillis ?? 0) }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="duration" v-if="item.attributes.playCount" @dblclick="route()">
|
||||||
|
{{ item.attributes.playCount }}
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -49,9 +49,21 @@
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
<div class="row" v-if="!seenReplay">
|
||||||
|
<div class="col">
|
||||||
|
<button class="md-btn md-btn-block md-btn-replay--hero" @click="$root.appRoute('replay')">Replay {{ year }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h3>{{app.getLz('home.madeForYou')}}</h3>
|
<div class="row">
|
||||||
|
<div class="col nopadding">
|
||||||
|
<h3>{{app.getLz('home.madeForYou')}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto nopadding flex-center">
|
||||||
|
<button class="md-btn md-btn-replay" v-if="seenReplay" @click="$root.appRoute('replay')">Replay {{ year }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<mediaitem-scroller-horizontal :items="madeForYou" v-if="isSectionReady('madeForYou')">
|
<mediaitem-scroller-horizontal :items="madeForYou" v-if="isSectionReady('madeForYou')">
|
||||||
</mediaitem-scroller-horizontal>
|
</mediaitem-scroller-horizontal>
|
||||||
|
@ -98,7 +110,9 @@
|
||||||
artistFeed: [],
|
artistFeed: [],
|
||||||
showingArtistFeed: false,
|
showingArtistFeed: false,
|
||||||
page: "main",
|
page: "main",
|
||||||
sectionsReady: []
|
sectionsReady: [],
|
||||||
|
year: new Date().getFullYear(),
|
||||||
|
seenReplay: localStorage.getItem('seenReplay')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
@ -106,6 +120,10 @@
|
||||||
this.getListenNowData()
|
this.getListenNowData()
|
||||||
await this.getArtistFeed()
|
await this.getArtistFeed()
|
||||||
await this.getFavorites()
|
await this.getFavorites()
|
||||||
|
if (new Date().getMonth() == 11) {
|
||||||
|
this.seenReplay = false
|
||||||
|
localStorage.setItem('seenReplay', false)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async seeAllHistory() {
|
async seeAllHistory() {
|
||||||
|
|
|
@ -15,8 +15,16 @@
|
||||||
v-model="library.songs.search" class="search-input">
|
v-model="library.songs.search" class="search-input">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto flex-center">
|
<div class="col-auto flex-center">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<button class="col md-btn md-btn-primary md-btn-icon" style="min-width: 100px;margin-right: 3px;"
|
||||||
|
@click="app.mk.shuffleMode = 0; play()"> <img class="md-ico-play">
|
||||||
|
{{app.getLz('term.play')}}
|
||||||
|
</button>
|
||||||
|
<button class="col md-btn md-btn-primary md-btn-icon" style="min-width: 100px;margin-right: 3px;"
|
||||||
|
@click="app.mk.shuffleMode = 1;play()"> <img class="md-ico-shuffle">
|
||||||
|
{{app.getLz('term.shuffle')}}
|
||||||
|
</button>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<select class="md-select" v-model="prefs.sort" @change="$root.searchLibrarySongs()">
|
<select class="md-select" v-model="prefs.sort" @change="$root.searchLibrarySongs()">
|
||||||
<optgroup :label="app.getLz('term.sortBy')">
|
<optgroup :label="app.getLz('term.sortBy')">
|
||||||
|
@ -75,6 +83,28 @@
|
||||||
methods: {
|
methods: {
|
||||||
sayHello: function () {
|
sayHello: function () {
|
||||||
alert('Hello world!');
|
alert('Hello world!');
|
||||||
|
},
|
||||||
|
play: function () {
|
||||||
|
|
||||||
|
function shuffleArray(array) {
|
||||||
|
for (var i = array.length - 1; i > 0; i--) {
|
||||||
|
var j = Math.floor(Math.random() * (i + 1));
|
||||||
|
var temp = array[i];
|
||||||
|
array[i] = array[j];
|
||||||
|
array[j] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = this.app.library.songs.displayListing.map(item => new MusicKit.MediaItem(item));
|
||||||
|
if (!app.mk.queue.isEmpty)
|
||||||
|
app.mk.queue.splice(0, app.mk.queue._itemIDs.length);
|
||||||
|
app.mk.stop().then(() => {
|
||||||
|
if (app.mk.shuffleMode == 1) {
|
||||||
|
shuffleArray(query)
|
||||||
|
}
|
||||||
|
app.mk.queue.append(query)
|
||||||
|
app.mk.changeToMediaAtIndex(0)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
144
src/renderer/views/pages/replay.ejs
Normal file
144
src/renderer/views/pages/replay.ejs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
<script type="text/x-template" id="replay-page">
|
||||||
|
<div class="content-inner replay-page">
|
||||||
|
<vue-horizontal style="height: 300px;">
|
||||||
|
<div class="replay-period" v-for="year in years" @click="getReplayYear(year.attributes.year)">
|
||||||
|
<div class="artwork-container">
|
||||||
|
<mediaitem-artwork :size="200" :url="year.relationships.playlist.data[0].attributes.artwork.url"></mediaitem-artwork>
|
||||||
|
</div>
|
||||||
|
{{ year.attributes.year }}
|
||||||
|
</div>
|
||||||
|
</vue-horizontal>
|
||||||
|
<hr>
|
||||||
|
<transition name="replaycard">
|
||||||
|
<div class="replay-viewport" v-if="loaded.id != -1">
|
||||||
|
<!-- Stats -->
|
||||||
|
<h1 class="replay-header">{{ loaded.attributes.year }} Replay</h1>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h4>{{ loaded.attributes.listenTimeInMinutes }} minutes</h4>
|
||||||
|
<h4>{{ loaded.attributes.uniqueAlbumCount }} Unique Albums</h4>
|
||||||
|
<h4>{{ loaded.attributes.uniqueArtistCount }} Unique Artists</h4>
|
||||||
|
<h4>{{ loaded.attributes.uniqueSongCount }} Unique Songs</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<mediaitem-square kind="card" :item="loaded.relationships.playlist.data[0]"></mediaitem-square>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Top Artists-->
|
||||||
|
<h3>Top Artists</h3>
|
||||||
|
<div class="well">
|
||||||
|
<mediaitem-scroller-horizontal>
|
||||||
|
<div class="card replay-card" v-for="artistData in loaded.views['top-artists'].data">
|
||||||
|
<div class="card-body">
|
||||||
|
<mediaitem-square :item="artistData.relationships.artist.data[0]"></mediaitem-square>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
{{ artistData.attributes.listenTimeInMinutes }} minutes<br>
|
||||||
|
Listened to: {{ artistData.attributes.playCount }} times
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mediaitem-scroller-horizontal>
|
||||||
|
</div>
|
||||||
|
<!-- Top Albums-->
|
||||||
|
<h3>Top Albums</h3>
|
||||||
|
<div class="well">
|
||||||
|
<mediaitem-scroller-horizontal>
|
||||||
|
<div class="card replay-card" v-for="albumData in loaded.views['top-albums'].data">
|
||||||
|
<div class="card-body">
|
||||||
|
<mediaitem-square :item="albumData.relationships.album.data[0]"></mediaitem-square>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
{{ albumData.attributes.listenTimeInMinutes }} minutes<br>
|
||||||
|
{{ albumData.attributes.playCount }} plays
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mediaitem-scroller-horizontal>
|
||||||
|
</div>
|
||||||
|
<!-- Top Songs-->
|
||||||
|
<h3>Top Songs</h3>
|
||||||
|
<div class="well">
|
||||||
|
<listitem-horizontal :show-library-status="false" :items="songsToArray(loaded.views['top-songs'].data)"></listitem-horizontal>
|
||||||
|
</div>
|
||||||
|
<h3>Top Genres</h3>
|
||||||
|
<div class="top-genres-container">
|
||||||
|
<div v-for="genre in loaded.topGenres" class="replay-genre-display">
|
||||||
|
<div class="genre-name">
|
||||||
|
{{ genre.genre }}
|
||||||
|
</div>
|
||||||
|
<div class="genre-count">
|
||||||
|
<div class="genre-count-bar" :style="{'width': genre.count + '%'}">
|
||||||
|
{{ genre.count }}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
Vue.component('replay-page', {
|
||||||
|
template: '#replay-page',
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
years: [],
|
||||||
|
loaded: {
|
||||||
|
id: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
// Get available years
|
||||||
|
let year = await app.mk.api.v3.music("/v1/me/music-summaries/search?extend=inLibrary&period=year&fields[music-summaries]=period%2Cyear&include[music-summaries]=playlist")
|
||||||
|
this.years = year.data.data
|
||||||
|
this.years.reverse()
|
||||||
|
localStorage.setItem("seenReplay", true)
|
||||||
|
this.getReplayYear();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
songsToArray(songsData) {
|
||||||
|
let songs = []
|
||||||
|
let topGenres = {}
|
||||||
|
let genrePlayCount = 0;
|
||||||
|
songsData.forEach(function (songData) {
|
||||||
|
let song = songData.relationships.song.data[0]
|
||||||
|
song.attributes.playCount = songData.attributes.playCount
|
||||||
|
songs.push(song)
|
||||||
|
genrePlayCount += song.attributes.playCount
|
||||||
|
song.attributes.genreNames.forEach(function (genre) {
|
||||||
|
if (genre != "Music") {
|
||||||
|
if (topGenres[genre] == undefined) {
|
||||||
|
topGenres[genre] = song.attributes.playCount
|
||||||
|
} else {
|
||||||
|
topGenres[genre] += song.attributes.playCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let topGenresArray = []
|
||||||
|
for (let genre in topGenres) {
|
||||||
|
topGenresArray.push({
|
||||||
|
genre: genre,
|
||||||
|
count: topGenres[genre]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
topGenresArray.sort(function (a, b) {
|
||||||
|
return b.count - a.count
|
||||||
|
})
|
||||||
|
topGenresArray.forEach(function (genre) {
|
||||||
|
genre.count = Math.round(genre.count / genrePlayCount * 100)
|
||||||
|
})
|
||||||
|
this.loaded.topGenres = topGenresArray
|
||||||
|
|
||||||
|
return songs
|
||||||
|
},
|
||||||
|
async getReplayYear(year = new Date().getFullYear()) {
|
||||||
|
this.loaded.id = -1
|
||||||
|
let response = await app.mk.api.v3.music(`/v1/me/music-summaries/year-${year}?extend=inLibrary&views=top-artists%2Ctop-albums%2Ctop-songs&include[music-summaries]=playlist&include[playlists]=tracks&includeOnly=playlist%2Ctracks%2Csong%2Cartist%2Calbum`)
|
||||||
|
console.warn(response.data.data[0])
|
||||||
|
this.loaded = response.data.data[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -645,6 +645,36 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="md-option-header">
|
||||||
|
<span>{{$root.getLz('settings.header.debug')}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-option-body">
|
||||||
|
<div class="md-option-line">
|
||||||
|
<div class="md-option-segment">
|
||||||
|
{{$root.getLz('settings.option.debug.copy_log')}}
|
||||||
|
</div>
|
||||||
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
|
<button class="md-btn" @click="copyLogs">
|
||||||
|
{{$root.getLz('action.copy')}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="md-option-line">
|
||||||
|
<div class="md-option-segment">
|
||||||
|
{{$root.getLz('settings.option.debug.openAppData')}}
|
||||||
|
</div>
|
||||||
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
|
<button class="md-btn" @click="openAppData">
|
||||||
|
{{$root.getLz('action.open')}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="md-option-header">
|
<div class="md-option-header">
|
||||||
<span>{{$root.getLz('settings.header.experimental')}}</span>
|
<span>{{$root.getLz('settings.header.experimental')}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -691,16 +721,32 @@
|
||||||
<input type="checkbox" v-model="app.cfg.general.close_button_hide" switch/>
|
<input type="checkbox" v-model="app.cfg.general.close_button_hide" switch/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-option-line">
|
<div class="md-option-line update-check">
|
||||||
<div class="md-option-segment">
|
<div class="md-option-segment">
|
||||||
{{$root.getLz('settings.option.experimental.copy_log')}}
|
{{$root.getLz('settings.option.general.updateCider')}}
|
||||||
</div>
|
</div>
|
||||||
<div class="md-option-segment md-option-segment_auto">
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
<button class="md-btn" @click="copyLogs">
|
<button class="md-btn" @click="app.checkForUpdate()">
|
||||||
{{$root.getLz('action.copy')}}
|
{{$root.getLz('term.check')}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="md-option-line update-check">
|
||||||
|
<div class="md-option-segment">
|
||||||
|
{{$root.getLz('settings.option.general.updateCider.branch')}}<br>
|
||||||
|
<small>({{$root.getLz('settings.option.general.updateCider.branch.description')}})</small>
|
||||||
|
</div>
|
||||||
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
|
<select class="md-select" style="width:180px;" v-model="app.cfg.general.update_branch">
|
||||||
|
<option value="main">
|
||||||
|
{{$root.getLz('settings.option.general.updateCider.branch.main')}}
|
||||||
|
</option>
|
||||||
|
<option value="develop">
|
||||||
|
{{$root.getLz('settings.option.general.updateCider.branch.develop')}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="opacity: 0.5; pointer-events: none">
|
<div style="opacity: 0.5; pointer-events: none">
|
||||||
<div class="md-option-header">
|
<div class="md-option-header">
|
||||||
|
@ -790,6 +836,9 @@
|
||||||
ipcRenderer.send('fetch-log')
|
ipcRenderer.send('fetch-log')
|
||||||
notyf.success(app.getLz('term.share.success'));
|
notyf.success(app.getLz('term.share.success'));
|
||||||
},
|
},
|
||||||
|
openAppData() {
|
||||||
|
ipcRenderer.send('open-appdata')
|
||||||
|
},
|
||||||
getLanguages: function () {
|
getLanguages: function () {
|
||||||
let langs = this.$root.lzListing
|
let langs = this.$root.lzListing
|
||||||
let categories = {
|
let categories = {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue