mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-24 12:30:38 -04:00
[web] Merge from main branch
This commit is contained in:
commit
42564905e0
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -219,6 +219,9 @@ static cfg_opt_t sec_spotify[] =
|
|||||||
CFG_BOOL("artist_override", cfg_false, CFGF_NONE),
|
CFG_BOOL("artist_override", cfg_false, CFGF_NONE),
|
||||||
CFG_BOOL("album_override", cfg_false, CFGF_NONE),
|
CFG_BOOL("album_override", cfg_false, CFGF_NONE),
|
||||||
CFG_BOOL("disable_legacy_mode", cfg_false, CFGF_NONE),
|
CFG_BOOL("disable_legacy_mode", cfg_false, CFGF_NONE),
|
||||||
|
// Issued by Spotify on developer.spotify.com for forked-daapd/OwnTone
|
||||||
|
CFG_STR("webapi_client_id", "0e684a5422384114a8ae7ac020f01789", CFGF_NONE),
|
||||||
|
CFG_STR("webapi_client_secret", "232af95f39014c9ba218285a5c11a239", CFGF_NONE),
|
||||||
CFG_END()
|
CFG_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,7 +22,9 @@ PKG_CHECK_MODULES([JSON_C], [json-c])
|
|||||||
PKG_CHECK_MODULES([LIBGCRYPT], [libgcrypt], [], [
|
PKG_CHECK_MODULES([LIBGCRYPT], [libgcrypt], [], [
|
||||||
AM_PATH_LIBGCRYPT([], [], [AC_MSG_ERROR([[libgcrypt is required]])])
|
AM_PATH_LIBGCRYPT([], [], [AC_MSG_ERROR([[libgcrypt is required]])])
|
||||||
])
|
])
|
||||||
PKG_CHECK_MODULES([LIBCURL], [libcurl])
|
|
||||||
|
dnl Need 7.83.0 for curl_easy_nextheader()
|
||||||
|
PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.83.0])
|
||||||
PKG_CHECK_MODULES([LIBPROTOBUF_C], [libprotobuf-c])
|
PKG_CHECK_MODULES([LIBPROTOBUF_C], [libprotobuf-c])
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile tests/Makefile])
|
AC_CONFIG_FILES([Makefile tests/Makefile])
|
||||||
|
@ -147,10 +147,10 @@ static int spotify_base_plid;
|
|||||||
// Flag to avoid triggering playlist change events while the (re)scan is running
|
// Flag to avoid triggering playlist change events while the (re)scan is running
|
||||||
static bool scanning;
|
static bool scanning;
|
||||||
|
|
||||||
|
|
||||||
// Endpoints and credentials for the web api
|
// Endpoints and credentials for the web api
|
||||||
static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789";
|
static const char *spotify_client_id;
|
||||||
static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239";
|
static const char *spotify_client_secret;
|
||||||
|
|
||||||
static const char *spotify_scope = "playlist-read-private playlist-read-collaborative user-library-read user-read-private streaming";
|
static const char *spotify_scope = "playlist-read-private playlist-read-collaborative user-library-read user-read-private streaming";
|
||||||
|
|
||||||
static const char *spotify_auth_uri = "https://accounts.spotify.com/authorize";
|
static const char *spotify_auth_uri = "https://accounts.spotify.com/authorize";
|
||||||
@ -2067,6 +2067,9 @@ spotifywebapi_library_init()
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
spotify_client_id = cfg_getstr(cfg_getsec(cfg, "spotify"), "webapi_client_id");
|
||||||
|
spotify_client_secret = cfg_getstr(cfg_getsec(cfg, "spotify"), "webapi_client_secret");
|
||||||
|
|
||||||
ret = spotify_init();
|
ret = spotify_init();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -182,6 +182,7 @@ int mpd_lex_parse(struct mpd_result *result, const char *input);
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define INVERT_MASK 0x80000000
|
#define INVERT_MASK 0x80000000
|
||||||
|
#define RECURSION_MAX 64
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dependencies, mocked or real */
|
/* Dependencies, mocked or real */
|
||||||
@ -225,6 +226,8 @@ struct mpd_result {
|
|||||||
|
|
||||||
int err;
|
int err;
|
||||||
char errmsg[128];
|
char errmsg[128];
|
||||||
|
|
||||||
|
int recursion_level;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mpd_type {
|
enum mpd_type {
|
||||||
@ -416,6 +419,15 @@ static void sql_append_recursive(struct mpd_result *result, struct mpd_result_pa
|
|||||||
{
|
{
|
||||||
char escape_char;
|
char escape_char;
|
||||||
|
|
||||||
|
if (result->recursion_level > RECURSION_MAX)
|
||||||
|
{
|
||||||
|
snprintf(result->errmsg, sizeof(result->errmsg), "Recursion maximum exceeded");
|
||||||
|
result->err = -2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->recursion_level++;
|
||||||
|
|
||||||
switch (append_type)
|
switch (append_type)
|
||||||
{
|
{
|
||||||
case SQL_APPEND_OPERATOR:
|
case SQL_APPEND_OPERATOR:
|
||||||
@ -477,6 +489,8 @@ static void sql_append_recursive(struct mpd_result *result, struct mpd_result_pa
|
|||||||
sql_append(result, part, ")");
|
sql_append(result, part, ")");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result->recursion_level--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates the parsing result from the AST. Errors are set via result->err. */
|
/* Creates the parsing result from the AST. Errors are set via result->err. */
|
||||||
|
@ -178,6 +178,7 @@ int rsp_lex_parse(struct rsp_result *result, const char *input);
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define INVERT_MASK 0x80000000
|
#define INVERT_MASK 0x80000000
|
||||||
|
#define RECURSION_MAX 64
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dependencies, mocked or real */
|
/* Dependencies, mocked or real */
|
||||||
@ -197,6 +198,8 @@ struct rsp_result {
|
|||||||
int offset;
|
int offset;
|
||||||
int err;
|
int err;
|
||||||
char errmsg[128];
|
char errmsg[128];
|
||||||
|
|
||||||
|
int recursion_level;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,6 +275,15 @@ static void sql_append_recursive(struct rsp_result *result, struct ast *a, const
|
|||||||
{
|
{
|
||||||
char escape_char;
|
char escape_char;
|
||||||
|
|
||||||
|
if (result->recursion_level > RECURSION_MAX)
|
||||||
|
{
|
||||||
|
snprintf(result->errmsg, sizeof(result->errmsg), "Recursion maximum exceeded");
|
||||||
|
result->err = -2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->recursion_level++;
|
||||||
|
|
||||||
switch (append_type)
|
switch (append_type)
|
||||||
{
|
{
|
||||||
case SQL_APPEND_OPERATOR:
|
case SQL_APPEND_OPERATOR:
|
||||||
@ -317,6 +329,8 @@ static void sql_append_recursive(struct rsp_result *result, struct ast *a, const
|
|||||||
sql_append(result, ")");
|
sql_append(result, ")");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result->recursion_level--;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sql_from_ast(struct rsp_result *result, struct ast *a)
|
static void sql_from_ast(struct rsp_result *result, struct ast *a)
|
||||||
|
@ -182,6 +182,7 @@ int smartpl_lex_parse(struct smartpl_result *result, const char *input);
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define INVERT_MASK 0x80000000
|
#define INVERT_MASK 0x80000000
|
||||||
|
#define RECURSION_MAX 64
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dependencies, mocked or real */
|
/* Dependencies, mocked or real */
|
||||||
@ -215,6 +216,8 @@ struct smartpl_result {
|
|||||||
int limit;
|
int limit;
|
||||||
int err;
|
int err;
|
||||||
char errmsg[128];
|
char errmsg[128];
|
||||||
|
|
||||||
|
int recursion_level;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,6 +292,15 @@ static void sql_append_recursive(struct smartpl_result *result, struct result_pa
|
|||||||
{
|
{
|
||||||
char escape_char;
|
char escape_char;
|
||||||
|
|
||||||
|
if (result->recursion_level > RECURSION_MAX)
|
||||||
|
{
|
||||||
|
snprintf(result->errmsg, sizeof(result->errmsg), "Recursion maximum exceeded");
|
||||||
|
result->err = -2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->recursion_level++;
|
||||||
|
|
||||||
switch (append_type)
|
switch (append_type)
|
||||||
{
|
{
|
||||||
case SQL_APPEND_OPERATOR:
|
case SQL_APPEND_OPERATOR:
|
||||||
@ -359,6 +371,8 @@ static void sql_append_recursive(struct smartpl_result *result, struct result_pa
|
|||||||
sql_append(result, part, "', ");
|
sql_append(result, part, "', ");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result->recursion_level--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates the parsing result from the AST. Errors are set via result->err. */
|
/* Creates the parsing result from the AST. Errors are set via result->err. */
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
class="media is-align-items-center mb-0"
|
||||||
|
:class="{ 'is-clickable': isPlayable, 'is-not-allowed': !isPlayable }"
|
||||||
@click="open"
|
@click="open"
|
||||||
>
|
>
|
||||||
<mdicon v-if="icon" class="media-left icon" :name="icon" />
|
<mdicon v-if="icon" class="media-left icon" :name="icon" />
|
||||||
@ -28,10 +29,14 @@
|
|||||||
'is-size-6': position === 0,
|
'is-size-6': position === 0,
|
||||||
'is-size-7': position !== 0,
|
'is-size-7': position !== 0,
|
||||||
'has-text-weight-bold': position !== 2,
|
'has-text-weight-bold': position !== 2,
|
||||||
'has-text-grey': position !== 0 || isRead
|
'has-text-grey': (position !== 0 || isRead) && isPlayable,
|
||||||
|
'has-text-grey-light': !isPlayable
|
||||||
}"
|
}"
|
||||||
v-text="line"
|
v-text="line"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="!isPlayable" class="is-size-7 has-text-grey">
|
||||||
|
<slot name="reason" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="progress" class="media-right">
|
<div v-if="progress" class="media-right">
|
||||||
<control-progress :value="progress" />
|
<control-progress :value="progress" />
|
||||||
@ -56,6 +61,7 @@ export default {
|
|||||||
image: { default: null, type: Object },
|
image: { default: null, type: Object },
|
||||||
index: { default: null, type: [String, Number] },
|
index: { default: null, type: [String, Number] },
|
||||||
isItem: { default: true, type: Boolean },
|
isItem: { default: true, type: Boolean },
|
||||||
|
isPlayable: { default: true, type: Boolean },
|
||||||
isRead: { default: false, type: Boolean },
|
isRead: { default: false, type: Boolean },
|
||||||
lines: { default: null, type: Array },
|
lines: { default: null, type: Array },
|
||||||
progress: { default: null, type: Number }
|
progress: { default: null, type: Number }
|
||||||
@ -71,3 +77,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.is-not-allowed {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,29 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.id">
|
<list-item
|
||||||
<div class="media is-align-items-center mb-0">
|
v-for="item in items"
|
||||||
<div
|
:key="item.id"
|
||||||
class="media-content"
|
:is-playable="item.is_playable"
|
||||||
:class="{
|
:lines="[item.name, item.artists[0].name, item.album.name]"
|
||||||
'is-clickable': item.is_playable,
|
@open="open(item)"
|
||||||
'is-not-allowed': !item.is_playable
|
@open-details="openDetails(item)"
|
||||||
}"
|
|
||||||
@click="play(item)"
|
|
||||||
>
|
>
|
||||||
<div
|
<template v-if="!item.is_playable" #reason>
|
||||||
class="is-size-6 has-text-weight-bold"
|
|
||||||
:class="{ 'has-text-grey-light': !item.is_playable }"
|
|
||||||
v-text="item.name"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="is-size-7 has-text-weight-bold"
|
|
||||||
:class="{
|
|
||||||
'has-text-grey': item.is_playable,
|
|
||||||
'has-text-grey-light': !item.is_playable
|
|
||||||
}"
|
|
||||||
v-text="item.artists[0].name"
|
|
||||||
/>
|
|
||||||
<div class="is-size-7 has-text-grey" v-text="item.album.name" />
|
|
||||||
<div v-if="!item.is_playable" class="is-size-7 has-text-grey">
|
|
||||||
(<span v-text="$t('list.spotify.not-playable-track')" />
|
(<span v-text="$t('list.spotify.not-playable-track')" />
|
||||||
<span
|
<span
|
||||||
v-if="item.restrictions?.reason"
|
v-if="item.restrictions?.reason"
|
||||||
@ -33,15 +17,8 @@
|
|||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>)
|
/>)
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDetails(item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
</list-item>
|
||||||
<modal-dialog-track-spotify
|
<modal-dialog-track-spotify
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@ -50,12 +27,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogTrackSpotify from '@/components/ModalDialogTrackSpotify.vue'
|
import ModalDialogTrackSpotify from '@/components/ModalDialogTrackSpotify.vue'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListTracksSpotify',
|
name: 'ListTracksSpotify',
|
||||||
components: { ModalDialogTrackSpotify },
|
components: { ListItem, ModalDialogTrackSpotify },
|
||||||
props: {
|
props: {
|
||||||
contextUri: { default: '', type: String },
|
contextUri: { default: '', type: String },
|
||||||
items: { required: true, type: Object }
|
items: { required: true, type: Object }
|
||||||
@ -64,11 +42,7 @@ export default {
|
|||||||
return { selectedItem: {}, showDetailsModal: false }
|
return { selectedItem: {}, showDetailsModal: false }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openDetails(item) {
|
open(item) {
|
||||||
this.selectedItem = item
|
|
||||||
this.showDetailsModal = true
|
|
||||||
},
|
|
||||||
play(item) {
|
|
||||||
if (item.is_playable) {
|
if (item.is_playable) {
|
||||||
webapi.player_play_uri(
|
webapi.player_play_uri(
|
||||||
this.contextUri || item.uri,
|
this.contextUri || item.uri,
|
||||||
@ -76,13 +50,11 @@ export default {
|
|||||||
item.position || 0
|
item.position || 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
openDetails(item) {
|
||||||
|
this.selectedItem = item
|
||||||
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.is-not-allowed {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user