[web] Merge from main branch

This commit is contained in:
Alain Nussbaumer 2025-03-24 09:19:17 +01:00
commit 42564905e0
10 changed files with 130 additions and 96 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -219,6 +219,9 @@ static cfg_opt_t sec_spotify[] =
CFG_BOOL("artist_override", cfg_false, CFGF_NONE),
CFG_BOOL("album_override", 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()
};

View File

@ -22,7 +22,9 @@ PKG_CHECK_MODULES([JSON_C], [json-c])
PKG_CHECK_MODULES([LIBGCRYPT], [libgcrypt], [], [
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])
AC_CONFIG_FILES([Makefile tests/Makefile])

View File

@ -147,10 +147,10 @@ static int spotify_base_plid;
// Flag to avoid triggering playlist change events while the (re)scan is running
static bool scanning;
// Endpoints and credentials for the web api
static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789";
static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239";
static const char *spotify_client_id;
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_auth_uri = "https://accounts.spotify.com/authorize";
@ -2067,6 +2067,9 @@ spotifywebapi_library_init()
{
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();
if (ret < 0)
return -1;

View File

@ -182,6 +182,7 @@ int mpd_lex_parse(struct mpd_result *result, const char *input);
#include <assert.h>
#define INVERT_MASK 0x80000000
#define RECURSION_MAX 64
}
/* Dependencies, mocked or real */
@ -225,6 +226,8 @@ struct mpd_result {
int err;
char errmsg[128];
int recursion_level;
};
enum mpd_type {
@ -416,6 +419,15 @@ static void sql_append_recursive(struct mpd_result *result, struct mpd_result_pa
{
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)
{
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, ")");
break;
}
result->recursion_level--;
}
/* Creates the parsing result from the AST. Errors are set via result->err. */

View File

@ -178,6 +178,7 @@ int rsp_lex_parse(struct rsp_result *result, const char *input);
#include <assert.h>
#define INVERT_MASK 0x80000000
#define RECURSION_MAX 64
}
/* Dependencies, mocked or real */
@ -197,6 +198,8 @@ struct rsp_result {
int offset;
int err;
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;
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)
{
case SQL_APPEND_OPERATOR:
@ -317,6 +329,8 @@ static void sql_append_recursive(struct rsp_result *result, struct ast *a, const
sql_append(result, ")");
break;
}
result->recursion_level--;
}
static void sql_from_ast(struct rsp_result *result, struct ast *a)

View File

@ -182,6 +182,7 @@ int smartpl_lex_parse(struct smartpl_result *result, const char *input);
#include <assert.h>
#define INVERT_MASK 0x80000000
#define RECURSION_MAX 64
}
/* Dependencies, mocked or real */
@ -215,6 +216,8 @@ struct smartpl_result {
int limit;
int err;
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;
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)
{
case SQL_APPEND_OPERATOR:
@ -359,6 +371,8 @@ static void sql_append_recursive(struct smartpl_result *result, struct result_pa
sql_append(result, part, "', ");
break;
}
result->recursion_level--;
}
/* Creates the parsing result from the AST. Errors are set via result->err. */

View File

@ -10,7 +10,8 @@
</div>
<div
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"
>
<mdicon v-if="icon" class="media-left icon" :name="icon" />
@ -28,10 +29,14 @@
'is-size-6': position === 0,
'is-size-7': position !== 0,
'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"
/>
<div v-if="!isPlayable" class="is-size-7 has-text-grey">
<slot name="reason" />
</div>
</div>
<div v-if="progress" class="media-right">
<control-progress :value="progress" />
@ -56,6 +61,7 @@ export default {
image: { default: null, type: Object },
index: { default: null, type: [String, Number] },
isItem: { default: true, type: Boolean },
isPlayable: { default: true, type: Boolean },
isRead: { default: false, type: Boolean },
lines: { default: null, type: Array },
progress: { default: null, type: Number }
@ -71,3 +77,9 @@ export default {
}
}
</script>
<style scoped>
.is-not-allowed {
cursor: not-allowed;
}
</style>

View File

@ -1,29 +1,13 @@
<template>
<template v-for="item in items" :key="item.id">
<div class="media is-align-items-center mb-0">
<div
class="media-content"
:class="{
'is-clickable': item.is_playable,
'is-not-allowed': !item.is_playable
}"
@click="play(item)"
<list-item
v-for="item in items"
:key="item.id"
:is-playable="item.is_playable"
:lines="[item.name, item.artists[0].name, item.album.name]"
@open="open(item)"
@open-details="openDetails(item)"
>
<div
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">
<template v-if="!item.is_playable" #reason>
(<span v-text="$t('list.spotify.not-playable-track')" />
<span
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>
</list-item>
<modal-dialog-track-spotify
:item="selectedItem"
:show="showDetailsModal"
@ -50,12 +27,13 @@
</template>
<script>
import ListItem from '@/components/ListItem.vue'
import ModalDialogTrackSpotify from '@/components/ModalDialogTrackSpotify.vue'
import webapi from '@/webapi'
export default {
name: 'ListTracksSpotify',
components: { ModalDialogTrackSpotify },
components: { ListItem, ModalDialogTrackSpotify },
props: {
contextUri: { default: '', type: String },
items: { required: true, type: Object }
@ -64,11 +42,7 @@ export default {
return { selectedItem: {}, showDetailsModal: false }
},
methods: {
openDetails(item) {
this.selectedItem = item
this.showDetailsModal = true
},
play(item) {
open(item) {
if (item.is_playable) {
webapi.player_play_uri(
this.contextUri || item.uri,
@ -76,13 +50,11 @@ export default {
item.position || 0
)
}
},
openDetails(item) {
this.selectedItem = item
this.showDetailsModal = true
}
}
}
</script>
<style scoped>
.is-not-allowed {
cursor: not-allowed;
}
</style>