[player/jsonapi/db] Add interface to get and set an output format
This commit is contained in:
parent
c079df5da7
commit
9f719ca155
|
@ -341,6 +341,7 @@ GET /api/outputs
|
|||
| requires_auth | boolean | `true` if output requires authentication |
|
||||
| needs_auth_key | boolean | `true` if output requires an authorization key (device verification) |
|
||||
| volume | integer | Volume in percent (0 - 100) |
|
||||
| format | string | Stream format |
|
||||
|
||||
**Example**
|
||||
|
||||
|
@ -359,7 +360,8 @@ curl -X GET "http://localhost:3689/api/outputs"
|
|||
"has_password": false,
|
||||
"requires_auth": false,
|
||||
"needs_auth_key": false,
|
||||
"volume": 0
|
||||
"volume": 0,
|
||||
"format": "alac"
|
||||
},
|
||||
{
|
||||
"id": "0",
|
||||
|
@ -369,7 +371,8 @@ curl -X GET "http://localhost:3689/api/outputs"
|
|||
"has_password": false,
|
||||
"requires_auth": false,
|
||||
"needs_auth_key": false,
|
||||
"volume": 19
|
||||
"volume": 19,
|
||||
"format": "pcm"
|
||||
},
|
||||
{
|
||||
"id": "100",
|
||||
|
@ -379,7 +382,8 @@ curl -X GET "http://localhost:3689/api/outputs"
|
|||
"has_password": false,
|
||||
"requires_auth": false,
|
||||
"needs_auth_key": false,
|
||||
"volume": 0
|
||||
"volume": 0,
|
||||
"format": "pcm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -448,6 +452,7 @@ curl -X GET "http://localhost:3689/api/outputs/0"
|
|||
"requires_auth": false,
|
||||
"needs_auth_key": false,
|
||||
"volume": 3
|
||||
"format": "pcm",
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -474,6 +479,7 @@ PUT /api/outputs/{id}
|
|||
| selected | boolean | *(Optional)* `true` to enable and `false` to disable the output |
|
||||
| volume | integer | *(Optional)* Volume in percent (0 - 100) |
|
||||
| pin | string | *(Optional)* PIN for device verification |
|
||||
| format | string | *(Optional)* Stream format |
|
||||
|
||||
**Response**
|
||||
|
||||
|
|
8
src/db.c
8
src/db.c
|
@ -4787,10 +4787,10 @@ db_admin_delete(const char *key)
|
|||
int
|
||||
db_speaker_save(struct output_device *device)
|
||||
{
|
||||
#define Q_TMPL "INSERT OR REPLACE INTO speakers (id, selected, volume, name, auth_key) VALUES (%" PRIi64 ", %d, %d, %Q, %Q);"
|
||||
#define Q_TMPL "INSERT OR REPLACE INTO speakers (id, selected, volume, name, auth_key, format) VALUES (%" PRIi64 ", %d, %d, %Q, %Q, %d);"
|
||||
char *query;
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL, device->id, device->selected, device->volume, device->name, device->auth_key);
|
||||
query = sqlite3_mprintf(Q_TMPL, device->id, device->selected, device->volume, device->name, device->auth_key, device->format);
|
||||
|
||||
return db_query_run(query, 1, 0);
|
||||
#undef Q_TMPL
|
||||
|
@ -4799,7 +4799,7 @@ db_speaker_save(struct output_device *device)
|
|||
int
|
||||
db_speaker_get(struct output_device *device, uint64_t id)
|
||||
{
|
||||
#define Q_TMPL "SELECT s.selected, s.volume, s.name, s.auth_key FROM speakers s WHERE s.id = %" PRIi64 ";"
|
||||
#define Q_TMPL "SELECT s.selected, s.volume, s.name, s.auth_key, s.format FROM speakers s WHERE s.id = %" PRIi64 ";"
|
||||
sqlite3_stmt *stmt;
|
||||
char *query;
|
||||
int ret;
|
||||
|
@ -4841,6 +4841,8 @@ db_speaker_get(struct output_device *device, uint64_t id)
|
|||
free(device->auth_key);
|
||||
device->auth_key = safe_strdup((char *)sqlite3_column_text(stmt, 3));
|
||||
|
||||
device->format = sqlite3_column_int(stmt, 4);
|
||||
|
||||
#ifdef DB_PROFILE
|
||||
while (db_blocking_step(stmt) == SQLITE_ROW)
|
||||
; /* EMPTY */
|
||||
|
|
|
@ -151,8 +151,9 @@
|
|||
" id INTEGER PRIMARY KEY NOT NULL," \
|
||||
" selected INTEGER NOT NULL," \
|
||||
" volume INTEGER NOT NULL," \
|
||||
" name VARCHAR(255) DEFAULT NULL," \
|
||||
" auth_key VARCHAR(2048) DEFAULT NULL" \
|
||||
" name VARCHAR(255) DEFAULT NULL," \
|
||||
" auth_key VARCHAR(2048) DEFAULT NULL," \
|
||||
" format INTEGER DEFAULT 0" \
|
||||
");"
|
||||
|
||||
#define T_INOTIFY \
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* is a major upgrade. In other words minor version upgrades permit downgrading
|
||||
* the server after the database was upgraded. */
|
||||
#define SCHEMA_VERSION_MAJOR 22
|
||||
#define SCHEMA_VERSION_MINOR 1
|
||||
#define SCHEMA_VERSION_MINOR 2
|
||||
|
||||
int
|
||||
db_init_indices(sqlite3 *hdl);
|
||||
|
|
|
@ -1246,6 +1246,24 @@ static const struct db_upgrade_query db_upgrade_v2201_queries[] =
|
|||
};
|
||||
|
||||
|
||||
/* ---------------------------- 22.01 -> 22.02 ------------------------------ */
|
||||
|
||||
#define U_v2202_ALTER_SPEAKERS_ADD_FORMAT \
|
||||
"ALTER TABLE speakers ADD COLUMN format INTEGER DEFAULT 0;"
|
||||
|
||||
#define U_v2202_SCVER_MAJOR \
|
||||
"UPDATE admin SET value = '22' WHERE key = 'schema_version_major';"
|
||||
#define U_v2202_SCVER_MINOR \
|
||||
"UPDATE admin SET value = '02' WHERE key = 'schema_version_minor';"
|
||||
|
||||
static const struct db_upgrade_query db_upgrade_v2202_queries[] =
|
||||
{
|
||||
{ U_v2202_ALTER_SPEAKERS_ADD_FORMAT, "alter table speakers add column format" },
|
||||
|
||||
{ U_v2202_SCVER_MAJOR, "set schema_version_major to 22" },
|
||||
{ U_v2202_SCVER_MINOR, "set schema_version_minor to 02" },
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------- Main upgrade handler -------------------------- */
|
||||
|
||||
|
@ -1464,6 +1482,13 @@ db_upgrade(sqlite3 *hdl, int db_ver)
|
|||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case 2201:
|
||||
ret = db_generic_upgrade(hdl, db_upgrade_v2202_queries, ARRAY_SIZE(db_upgrade_v2202_queries));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
/* Last case statement is the only one that ends with a break statement! */
|
||||
break;
|
||||
|
||||
|
|
|
@ -1522,6 +1522,40 @@ struct outputs_param
|
|||
int output_volume;
|
||||
};
|
||||
|
||||
static enum player_format
|
||||
plformat_from_string(const char *format)
|
||||
{
|
||||
if (strcmp(format, "pcm") == 0)
|
||||
return PLAYER_FORMAT_PCM;
|
||||
if (strcmp(format, "wav") == 0)
|
||||
return PLAYER_FORMAT_WAV;
|
||||
if (strcmp(format, "mp3") == 0)
|
||||
return PLAYER_FORMAT_MP3;
|
||||
if (strcmp(format, "alac") == 0)
|
||||
return PLAYER_FORMAT_ALAC;
|
||||
if (strcmp(format, "opus") == 0)
|
||||
return PLAYER_FORMAT_OPUS;
|
||||
|
||||
return PLAYER_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
static const char *
|
||||
plformat_to_string(enum player_format format)
|
||||
{
|
||||
if (format == PLAYER_FORMAT_PCM)
|
||||
return "pcm";
|
||||
if (format == PLAYER_FORMAT_WAV)
|
||||
return "wav";
|
||||
if (format == PLAYER_FORMAT_MP3)
|
||||
return "mp3";
|
||||
if (format == PLAYER_FORMAT_ALAC)
|
||||
return "alac";
|
||||
if (format == PLAYER_FORMAT_OPUS)
|
||||
return "opus";
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static json_object *
|
||||
speaker_to_json(struct player_speaker_info *spk)
|
||||
{
|
||||
|
@ -1539,6 +1573,7 @@ speaker_to_json(struct player_speaker_info *spk)
|
|||
json_object_object_add(output, "requires_auth", json_object_new_boolean(spk->requires_auth));
|
||||
json_object_object_add(output, "needs_auth_key", json_object_new_boolean(spk->needs_auth_key));
|
||||
json_object_object_add(output, "volume", json_object_new_int(spk->absvol));
|
||||
json_object_object_add(output, "format", json_object_new_string(plformat_to_string(spk->format)));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -1602,6 +1637,7 @@ jsonapi_reply_outputs_put_byid(struct httpd_request *hreq)
|
|||
bool selected;
|
||||
int volume;
|
||||
const char *pin;
|
||||
const char *format;
|
||||
int ret;
|
||||
|
||||
ret = safe_atou64(hreq->path_parts[2], &output_id);
|
||||
|
@ -1644,6 +1680,13 @@ jsonapi_reply_outputs_put_byid(struct httpd_request *hreq)
|
|||
ret = player_speaker_authorize(output_id, pin);
|
||||
}
|
||||
|
||||
if (ret == 0 && jparse_contains_key(request, "format", json_type_string))
|
||||
{
|
||||
format = jparse_str_from_obj(request, "format");
|
||||
if (format)
|
||||
ret = player_speaker_format_set(output_id, plformat_from_string(format));
|
||||
}
|
||||
|
||||
jparse_free(request);
|
||||
|
||||
if (ret < 0)
|
||||
|
|
40
src/player.c
40
src/player.c
|
@ -2526,6 +2526,7 @@ device_to_speaker_info(struct player_speaker_info *spk, struct output_device *de
|
|||
spk->output_type[sizeof(spk->output_type) - 1] = '\0';
|
||||
spk->relvol = device->relvol;
|
||||
spk->absvol = device->volume;
|
||||
spk->format = device->format;
|
||||
|
||||
spk->selected = OUTPUTS_DEVICE_DISPLAY_SELECTED(device);
|
||||
|
||||
|
@ -2730,6 +2731,8 @@ speaker_prevent_playback_set(void *arg, int *retval)
|
|||
struct speaker_attr_param *param = arg;
|
||||
struct output_device *device;
|
||||
|
||||
*retval = -1;
|
||||
|
||||
device = outputs_device_get(param->spk_id);
|
||||
if (!device)
|
||||
return COMMAND_END;
|
||||
|
@ -2778,6 +2781,8 @@ speaker_busy_set(void *arg, int *retval)
|
|||
struct speaker_attr_param *param = arg;
|
||||
struct output_device *device;
|
||||
|
||||
*retval = -1;
|
||||
|
||||
device = outputs_device_get(param->spk_id);
|
||||
if (!device)
|
||||
return COMMAND_END;
|
||||
|
@ -2803,6 +2808,27 @@ speaker_busy_set(void *arg, int *retval)
|
|||
return COMMAND_END;
|
||||
}
|
||||
|
||||
static enum command_state
|
||||
speaker_format_set(void *arg, int *retval)
|
||||
{
|
||||
struct speaker_attr_param *param = arg;
|
||||
struct output_device *device;
|
||||
|
||||
*retval = -1;
|
||||
|
||||
if (param->format == PLAYER_FORMAT_UNKNOWN)
|
||||
return COMMAND_END;
|
||||
|
||||
device = outputs_device_get(param->spk_id);
|
||||
if (!device)
|
||||
return COMMAND_END;
|
||||
|
||||
device->format = param->format;
|
||||
|
||||
*retval = 0;
|
||||
return COMMAND_END;
|
||||
}
|
||||
|
||||
// Attempts to reactivate a speaker that has failed. That includes restarting
|
||||
// playback if it was stopped.
|
||||
static enum command_state
|
||||
|
@ -3466,14 +3492,22 @@ int
|
|||
player_speaker_authorize(uint64_t id, const char *pin)
|
||||
{
|
||||
struct speaker_attr_param param;
|
||||
int ret;
|
||||
|
||||
param.spk_id = id;
|
||||
param.pin = pin;
|
||||
|
||||
ret = commands_exec_sync(cmdbase, speaker_authorize, speaker_generic_bh, ¶m);
|
||||
return commands_exec_sync(cmdbase, speaker_authorize, speaker_generic_bh, ¶m);
|
||||
}
|
||||
|
||||
return ret;
|
||||
int
|
||||
player_speaker_format_set(uint64_t id, enum player_format format)
|
||||
{
|
||||
struct speaker_attr_param param;
|
||||
|
||||
param.spk_id = id;
|
||||
param.format = format;
|
||||
|
||||
return commands_exec_sync(cmdbase, speaker_format_set, NULL, ¶m);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
12
src/player.h
12
src/player.h
|
@ -29,7 +29,12 @@ enum player_seek_mode {
|
|||
};
|
||||
|
||||
enum player_format {
|
||||
PLAYER_FORMAT_MP3,
|
||||
PLAYER_FORMAT_UNKNOWN = -1,
|
||||
PLAYER_FORMAT_PCM = 0,
|
||||
PLAYER_FORMAT_WAV = 1,
|
||||
PLAYER_FORMAT_MP3 = 2,
|
||||
PLAYER_FORMAT_ALAC = 3,
|
||||
PLAYER_FORMAT_OPUS = 4,
|
||||
};
|
||||
|
||||
struct player_speaker_info {
|
||||
|
@ -40,6 +45,8 @@ struct player_speaker_info {
|
|||
int relvol;
|
||||
int absvol;
|
||||
|
||||
enum player_format format;
|
||||
|
||||
bool selected;
|
||||
bool has_password;
|
||||
bool requires_auth;
|
||||
|
@ -122,6 +129,9 @@ player_speaker_resurrect(void *arg);
|
|||
int
|
||||
player_speaker_authorize(uint64_t id, const char *pin);
|
||||
|
||||
int
|
||||
player_speaker_format_set(uint64_t id, enum player_format format);
|
||||
|
||||
int
|
||||
player_streaming_register(int *audio_fd, int *metadata_fd, enum player_format format, struct media_quality quality);
|
||||
|
||||
|
|
Loading…
Reference in New Issue