diff --git a/xbmc/ApplicationMessenger.cpp b/xbmc/ApplicationMessenger.cpp index 74a0a6b2b1398..54e6c98b40383 100644 --- a/xbmc/ApplicationMessenger.cpp +++ b/xbmc/ApplicationMessenger.cpp @@ -331,6 +331,13 @@ case TMSG_POWERDOWN: g_application.PlayMedia(*((*list)[0]), playlist); else { + // Handle "shuffled" option if present + if (list->HasProperty("shuffled") && list->GetProperty("shuffled").isBoolean()) + g_playlistPlayer.SetShuffle(playlist, list->GetProperty("shuffled").asBoolean(), false); + // Handle "repeat" option if present + if (list->HasProperty("repeat") && list->GetProperty("repeat").isInteger()) + g_playlistPlayer.SetRepeat(playlist, (PLAYLIST::REPEAT_STATE)list->GetProperty("repeat").asInteger(), false); + g_playlistPlayer.Add(playlist, (*list)); g_playlistPlayer.Play(pMsg->dwParam1); } diff --git a/xbmc/interfaces/json-rpc/PlayerOperations.cpp b/xbmc/interfaces/json-rpc/PlayerOperations.cpp index 42ae1e48c8400..4846c85b18884 100644 --- a/xbmc/interfaces/json-rpc/PlayerOperations.cpp +++ b/xbmc/interfaces/json-rpc/PlayerOperations.cpp @@ -279,8 +279,7 @@ JSONRPC_STATUS CPlayerOperations::Seek(const CStdString &method, ITransportLayer case Video: case Audio: if (parameterObject["value"].isObject()) - g_application.SeekTime(((parameterObject["value"]["hours"].asInteger() * 60) + parameterObject["value"]["minutes"].asInteger()) * 60 + - parameterObject["value"]["seconds"].asInteger() + ((double)parameterObject["value"]["milliseconds"].asInteger() / 1000.0)); + g_application.SeekTime(ParseTimeInSeconds(parameterObject["value"])); else if (IsType(parameterObject["value"], NumberValue)) g_application.SeekPercentage(parameterObject["value"].asFloat()); else if (parameterObject["value"].isString()) @@ -442,19 +441,27 @@ JSONRPC_STATUS CPlayerOperations::Rotate(const CStdString &method, ITransportLay JSONRPC_STATUS CPlayerOperations::Open(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { + CVariant optionShuffled = parameterObject["options"]["shuffled"]; + CVariant optionRepeat = parameterObject["options"]["repeat"]; + CVariant optionResume = parameterObject["options"]["resume"]; + if (parameterObject["item"].isObject() && parameterObject["item"].isMember("playlistid")) { int playlistid = (int)parameterObject["item"]["playlistid"].asInteger(); + + if (playlistid < PLAYLIST_PICTURE) + { + // Apply the "shuffled" option if available + if (optionShuffled.isBoolean()) + g_playlistPlayer.SetShuffle(playlistid, optionShuffled.asBoolean(), false); + // Apply the "repeat" option if available + if (!optionRepeat.isNull()) + g_playlistPlayer.SetRepeat(playlistid, (REPEAT_STATE)ParseRepeatState(optionRepeat), false); + } + switch (playlistid) { case PLAYLIST_MUSIC: - if (g_playlistPlayer.GetCurrentPlaylist() != playlistid) - g_playlistPlayer.SetCurrentPlaylist(playlistid); - - g_application.getApplicationMessenger().PlayListPlayerPlay((int)parameterObject["item"]["position"].asInteger()); - OnPlaylistChanged(); - break; - case PLAYLIST_VIDEO: g_application.getApplicationMessenger().MediaPlay(playlistid, (int)parameterObject["item"]["position"].asInteger()); OnPlaylistChanged(); @@ -473,7 +480,8 @@ JSONRPC_STATUS CPlayerOperations::Open(const CStdString &method, ITransportLayer exec += parameterObject["item"]["path"].asString(); - if (parameterObject["item"]["random"].asBoolean()) + if ((optionShuffled.isBoolean() && optionShuffled.asBoolean()) || + (!optionShuffled.isBoolean() && parameterObject["item"]["random"].asBoolean())) exec += ", random"; else exec += ", notrandom"; @@ -513,10 +521,32 @@ JSONRPC_STATUS CPlayerOperations::Open(const CStdString &method, ITransportLayer for (int index = 0; index < list.Size(); index++) slideshow->Add(list[index].get()); + if (optionShuffled.isBoolean() && optionShuffled.asBoolean()) + slideshow->Shuffle(); + return StartSlideshow(); } else + { + // Handle "shuffled" option + if (optionShuffled.isBoolean()) + list.SetProperty("shuffled", optionShuffled); + // Handle "repeat" option + if (!optionRepeat.isNull()) + list.SetProperty("repeat", ParseRepeatState(optionRepeat)); + // Handle "resume" option + if (list.Size() == 1) + { + if (optionResume.isBoolean() && optionResume.asBoolean()) + list[0]->m_lStartOffset = STARTOFFSET_RESUME; + else if (optionResume.isDouble()) + list[0]->SetProperty("StartPercent", optionResume); + else if (optionResume.isObject()) + list[0]->m_lStartOffset = (int)(ParseTimeInSeconds(optionResume) * 75.0); + } + g_application.getApplicationMessenger().MediaPlay(list); + } return ACK; } @@ -629,19 +659,11 @@ JSONRPC_STATUS CPlayerOperations::UnShuffle(const CStdString &method, ITransport JSONRPC_STATUS CPlayerOperations::Repeat(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { - REPEAT_STATE state = REPEAT_NONE; - std::string strState = parameterObject["state"].asString(); - switch (GetPlayer(parameterObject["playerid"])) { case Video: case Audio: - if (strState.compare("one") == 0) - state = REPEAT_ONE; - else if (strState.compare("all") == 0) - state = REPEAT_ALL; - - g_application.getApplicationMessenger().PlayListPlayerRepeat(GetPlaylist(GetPlayer(parameterObject["playerid"])), state); + g_application.getApplicationMessenger().PlayListPlayerRepeat(GetPlaylist(GetPlayer(parameterObject["playerid"])), (REPEAT_STATE)ParseRepeatState(parameterObject["state"])); OnPlaylistChanged(); break; @@ -1318,3 +1340,34 @@ JSONRPC_STATUS CPlayerOperations::GetPropertyValue(PlayerType player, const CStd return OK; } + +int CPlayerOperations::ParseRepeatState(const CVariant &repeat) +{ + REPEAT_STATE state = REPEAT_NONE; + std::string strState = repeat.asString(); + + if (strState.compare("one") == 0) + state = REPEAT_ONE; + else if (strState.compare("all") == 0) + state = REPEAT_ALL; + + return state; +} + +double CPlayerOperations::ParseTimeInSeconds(const CVariant &time) +{ + double seconds = 0.0; + if (time.isObject()) + { + if (time.isMember("hours")) + seconds += time["hours"].asInteger() * 60 * 60; + if (time.isMember("minutes")) + seconds += time["minutes"].asInteger() * 60; + if (time.isMember("seconds")) + seconds += time["seconds"].asInteger(); + if (time.isMember("milliseconds")) + seconds += time["milliseconds"].asDouble() / 1000.0; + } + + return seconds; +} diff --git a/xbmc/interfaces/json-rpc/PlayerOperations.h b/xbmc/interfaces/json-rpc/PlayerOperations.h index fac1241343f2d..ebe45e2c37041 100644 --- a/xbmc/interfaces/json-rpc/PlayerOperations.h +++ b/xbmc/interfaces/json-rpc/PlayerOperations.h @@ -76,5 +76,8 @@ namespace JSONRPC static void SendSlideshowAction(int actionID); static void OnPlaylistChanged(); static JSONRPC_STATUS GetPropertyValue(PlayerType player, const CStdString &property, CVariant &result); + + static int ParseRepeatState(const CVariant &repeat); + static double ParseTimeInSeconds(const CVariant &time); }; } diff --git a/xbmc/interfaces/json-rpc/ServiceDescription.h b/xbmc/interfaces/json-rpc/ServiceDescription.h index bb39e21559802..0d4ebd0f6a974 100644 --- a/xbmc/interfaces/json-rpc/ServiceDescription.h +++ b/xbmc/interfaces/json-rpc/ServiceDescription.h @@ -136,6 +136,16 @@ namespace JSONRPC "\"minimum\": 0.0," "\"maximum\": 100.0" "}", + "\"Player.Position.Time\": {" + "\"type\": \"object\"," + "\"additionalProperties\": false," + "\"properties\": {" + "\"hours\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 23, \"default\": 0 }," + "\"minutes\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 59, \"default\": 0 }," + "\"seconds\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 59, \"default\": 0 }," + "\"milliseconds\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 999, \"default\": 0 }" + "}" + "}", "\"Player.Speed\": {" "\"type\": \"object\"," "\"required\": true," @@ -951,11 +961,24 @@ namespace JSONRPC "{ \"type\": \"object\", \"required\": true, \"additionalProperties\": false," "\"properties\": {" "\"path\": { \"type\": \"string\", \"required\": true }," - "\"random\": { \"type\": \"boolean\", \"default\": true }," + "\"random\": { \"type\": \"boolean\", \"default\": true, \"description\": \"Deprecated, use the shuffled property of the options parameter instead\" }," "\"recursive\": { \"type\": \"boolean\", \"default\": true }" "}" "}" "]" + "}," + "{ \"name\": \"options\", \"type\": \"object\", \"additionalProperties\": false," + "\"properties\": {" + "\"shuffled\": { \"$ref\": \"Optional.Boolean\" }," + "\"repeat\": { \"type\": [ \"null\", \"Player.Repeat\" ], \"default\": null }," + "\"resume\": { \"type\": [" + "{ \"type\": \"boolean\", \"required\": true, \"description\": \"Whether to resume from the resume point or not\" }," + "{ \"$ref\": \"Player.Position.Percentage\", \"required\": true, \"description\": \"Percentage value to start from\" }," + "{ \"$ref\": \"Player.Position.Time\", \"required\": true, \"description\": \"Time to start from\" }" + "]," + "\"default\": false" + "}" + "}" "}" "]," "\"returns\": \"string\"" @@ -1050,14 +1073,7 @@ namespace JSONRPC "{ \"name\": \"playerid\", \"$ref\": \"Player.Id\", \"required\": true }," "{ \"name\": \"value\", \"required\": true, \"type\": [" "{ \"$ref\": \"Player.Position.Percentage\", \"required\": true, \"description\": \"Percentage value to seek to\" }," - "{ \"type\": \"object\", \"additionalProperties\": false, \"required\": true, \"description\": \"Time to seek to\"," - "\"properties\": {" - "\"hours\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 23 }," - "\"minutes\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 59 }," - "\"seconds\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 59 }," - "\"milliseconds\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 999 }" - "}" - "}," + "{ \"$ref\": \"Player.Position.Time\", \"required\": true, \"description\": \"Time to seek to\" }," "{ \"type\": \"string\", \"enum\": [ \"smallforward\", \"smallbackward\", \"bigforward\", \"bigbackward\" ], \"required\": true, \"description\": \"Seek by predefined jumps\" }" "]" "}" diff --git a/xbmc/interfaces/json-rpc/methods.json b/xbmc/interfaces/json-rpc/methods.json index abc5ea992d2e5..4d42417bdae8d 100644 --- a/xbmc/interfaces/json-rpc/methods.json +++ b/xbmc/interfaces/json-rpc/methods.json @@ -114,11 +114,24 @@ { "type": "object", "required": true, "additionalProperties": false, "properties": { "path": { "type": "string", "required": true }, - "random": { "type": "boolean", "default": true }, + "random": { "type": "boolean", "default": true, "description": "Deprecated, use the shuffled property of the options parameter instead" }, "recursive": { "type": "boolean", "default": true } } } ] + }, + { "name": "options", "type": "object", "additionalProperties": false, + "properties": { + "shuffled": { "$ref": "Optional.Boolean" }, + "repeat": { "type": [ "null", "Player.Repeat" ], "default": null }, + "resume": { "type": [ + { "type": "boolean", "required": true, "description": "Whether to resume from the resume point or not" }, + { "$ref": "Player.Position.Percentage", "required": true, "description": "Percentage value to start from" }, + { "$ref": "Player.Position.Time", "required": true, "description": "Time to start from" } + ], + "default": false + } + } } ], "returns": "string" @@ -213,14 +226,7 @@ { "name": "playerid", "$ref": "Player.Id", "required": true }, { "name": "value", "required": true, "type": [ { "$ref": "Player.Position.Percentage", "required": true, "description": "Percentage value to seek to" }, - { "type": "object", "additionalProperties": false, "required": true, "description": "Time to seek to", - "properties": { - "hours": { "type": "integer", "minimum": 0, "maximum": 23 }, - "minutes": { "type": "integer", "minimum": 0, "maximum": 59 }, - "seconds": { "type": "integer", "minimum": 0, "maximum": 59 }, - "milliseconds": { "type": "integer", "minimum": 0, "maximum": 999 } - } - }, + { "$ref": "Player.Position.Time", "required": true, "description": "Time to seek to" }, { "type": "string", "enum": [ "smallforward", "smallbackward", "bigforward", "bigbackward" ], "required": true, "description": "Seek by predefined jumps" } ] } diff --git a/xbmc/interfaces/json-rpc/types.json b/xbmc/interfaces/json-rpc/types.json index 6ba96dba609d3..75786608008cc 100644 --- a/xbmc/interfaces/json-rpc/types.json +++ b/xbmc/interfaces/json-rpc/types.json @@ -108,6 +108,16 @@ "minimum": 0.0, "maximum": 100.0 }, + "Player.Position.Time": { + "type": "object", + "additionalProperties": false, + "properties": { + "hours": { "type": "integer", "minimum": 0, "maximum": 23, "default": 0 }, + "minutes": { "type": "integer", "minimum": 0, "maximum": 59, "default": 0 }, + "seconds": { "type": "integer", "minimum": 0, "maximum": 59, "default": 0 }, + "milliseconds": { "type": "integer", "minimum": 0, "maximum": 999, "default": 0 } + } + }, "Player.Speed": { "type": "object", "required": true,