read language choice from setProperty?/MediaSelectionOptions

This commit is contained in:
F. Duncanh
2025-11-17 11:33:35 -05:00
parent 62214af028
commit f8c7b54241
3 changed files with 118 additions and 3 deletions

View File

@@ -39,6 +39,8 @@ struct airplay_video_s {
char apple_session_id[37];
char playback_uuid[37];
char *uri_prefix;
char *language_name;
char *language_code;
const char *lang;
char local_uri_prefix[23];
int next_uri;
@@ -88,6 +90,10 @@ airplay_video_t *airplay_video_init(raop_t *raop, unsigned short http_port,
airplay_video->start_position_seconds = 0.0f;
airplay_video->uri_prefix = NULL;
airplay_video->language_code = NULL;
airplay_video->language_name = NULL;
airplay_video->media_data_store = NULL;
airplay_video->master_playlist = NULL;
airplay_video->num_uri = 0;
@@ -103,6 +109,12 @@ airplay_video_destroy(airplay_video_t *airplay_video)
if (airplay_video->uri_prefix) {
free(airplay_video->uri_prefix);
}
if (airplay_video->language_name) {
free(airplay_video->language_name);
}
if (airplay_video->language_code) {
free(airplay_video->language_code);
}
if (airplay_video->media_data_store) {
destroy_media_data_store(airplay_video);
}
@@ -152,7 +164,29 @@ void set_uri_prefix(airplay_video_t *airplay_video, char *uri_prefix) {
}
const char *get_uri_prefix(airplay_video_t *airplay_video) {
return airplay_video->uri_prefix;
return (const char *) airplay_video->uri_prefix;
}
void set_language_name(airplay_video_t *airplay_video, char *language_name) {
if (airplay_video->language_name) {
free (airplay_video->language_name);
}
airplay_video->language_name = language_name;
}
const char *get_language_name(airplay_video_t *airplay_video) {
return (const char *)airplay_video->language_name;
}
void set_language_code(airplay_video_t *airplay_video, char *language_code) {
if (airplay_video->language_code) {
free (airplay_video->language_code);
}
airplay_video->language_code = language_code;
}
const char *get_language_code(airplay_video_t *airplay_video) {
return (const char *) airplay_video->language_code;
}
char *get_uri_local_prefix(airplay_video_t *airplay_video) {

View File

@@ -36,6 +36,10 @@ const char *get_playback_uuid(airplay_video_t *airplay_video);
void set_uri_prefix(airplay_video_t *airplay_video, char *uri_prefix);
const char *get_uri_prefix(airplay_video_t *airplay_video);
char *get_uri_local_prefix(airplay_video_t *airplay_video);
void set_language_code(airplay_video_t *airplay_video, char *language_code);
const char *get_language_code(airplay_video_t *airplay_video);
void set_language_name(airplay_video_t *airplay_video, char *language_name);
const char *get_language_name(airplay_video_t *airplay_video);
int get_next_FCUP_RequestID(airplay_video_t *airplay_video);
void set_next_media_uri_id(airplay_video_t *airplay_video, int id);

View File

@@ -168,6 +168,7 @@ http_handler_set_property(raop_conn_t *conn,
const char *property = url + strlen("/setProperty?");
logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_set_property: %s", property);
/* actionAtItemEnd: values:
0: advance (advance to next item, if there is one)
1: pause (pause playing)
@@ -175,9 +176,82 @@ http_handler_set_property(raop_conn_t *conn,
reverseEndTime (only used when rate < 0) time at which reverse playback ends
forwardEndTime (only used when rate > 0) time at which reverse playback ends
selectedMediaArray contains plist with language choice:
*/
if (!strcmp(property, "reverseEndTime") ||
airplay_video_t *airplay_video = conn->raop->airplay_video[conn->raop->current_video];
if (!strcmp(property, "selectedMediaArray")) {
/* verify that this request contains a binary plist*/
char *header_str = NULL;
int request_datalen = 0;
http_request_get_header_string(request, &header_str);
bool is_plist = strstr(header_str,"apple-binary-plist");
free(header_str);
if (!is_plist) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "POST /setProperty?selectedMediaArray"
"does not provide an apple-binary-plist");
goto post_error;
}
const char *request_data = http_request_get_data(request, &request_datalen);
plist_t req_root_node = NULL;
plist_from_bin(request_data, request_datalen, &req_root_node);
plist_t req_value_node = plist_dict_get_item(req_root_node, "value");
if (!req_value_node || !PLIST_IS_ARRAY(req_value_node)) {
logger_log(conn->raop->logger, LOGGER_INFO, "POST /setProperty?selectedMediaArray"
" did not provide expected plist from client");
goto post_error;
}
int count = plist_array_get_size(req_value_node);
char *name = NULL;
char *code = NULL;
char *language_name = NULL;
char *language_code = NULL;
for (int i = 0; i < count; i++) {
plist_t req_value_array_node = plist_array_get_item(req_value_node,i);
if (!language_name) {
plist_t req_value_options_name_node = plist_dict_get_item(req_value_array_node,"MediaSelectionOptionsName");
if (PLIST_IS_STRING(req_value_options_name_node)) {
plist_get_string_val(req_value_options_name_node, &name);
if (name) {
language_name = (char *) calloc(strlen(name) + 1, sizeof(char));
memcpy(language_name, name, strlen(name));
plist_mem_free(name);
}
}
}
if (!language_code) {
plist_t req_value_options_code_node = plist_dict_get_item(req_value_array_node,"MediaSelectionOptionsUnicodeLanguageIdentifier");
if (PLIST_IS_STRING(req_value_options_code_node)) {
plist_get_string_val(req_value_options_code_node, &code);
if (code) {
language_code = (char *) calloc(strlen(code) + 1, sizeof(char));
memcpy(language_code, code, strlen(code));
plist_mem_free(code);
}
}
}
if (language_code && language_name) {
break;
} else {
plist_free (req_value_array_node);
continue;
}
}
plist_free (req_root_node);
const char *lname = NULL, *lcode = NULL;
if (language_code) {
set_language_code(airplay_video, language_code);
lcode = get_language_code(airplay_video);
}
if (language_name) {
set_language_name(airplay_video, language_name);
lname = get_language_name(airplay_video);
}
logger_log(conn->raop->logger, LOGGER_INFO, "stored language from MediaSelectionOptions: %s \"%s\"", lcode, lname);
} else if (!strcmp(property, "reverseEndTime") ||
!strcmp(property, "forwardEndTime") ||
!strcmp(property, "actionAtItemEnd")) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "property %s is known but unhandled", property);
@@ -190,8 +264,11 @@ http_handler_set_property(raop_conn_t *conn,
http_response_add_header(response, "Content-Type", "text/x-apple-plist+xml");
} else {
logger_log(conn->raop->logger, LOGGER_DEBUG, "property %s is unknown, unhandled", property);
http_response_add_header(response, "Content-Length", "0");
goto post_error;
}
return;
post_error:
http_response_add_header(response, "Content-Length", "0");
}
/* handles GET /getProperty http requests from Client to Server. (not implemented) */