2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "PlayerBuiltins.h"
11 #include "Application.h"
13 #include "GUIUserMessages.h"
14 #include "PartyModeManager.h"
15 #include "PlayListPlayer.h"
16 #include "SeekHandler.h"
17 #include "ServiceBroker.h"
18 #include "filesystem/Directory.h"
19 #include "guilib/GUIComponent.h"
20 #include "guilib/GUIWindowManager.h"
21 #include "pvr/PVRManager.h"
22 #include "pvr/channels/PVRChannel.h"
23 #include "pvr/guilib/PVRGUIActions.h"
24 #include "pvr/recordings/PVRRecording.h"
25 #include "settings/MediaSettings.h"
26 #include "settings/Settings.h"
27 #include "settings/SettingsComponent.h"
28 #include "storage/MediaManager.h"
29 #include "utils/FileExtensionProvider.h"
30 #include "utils/StringUtils.h"
31 #include "utils/URIUtils.h"
32 #include "utils/log.h"
33 #include "video/PlayerController.h"
34 #include "video/windows/GUIWindowVideoBase.h"
35 #include "view/GUIViewState.h"
43 /*! \brief Clear current playlist
44 * \param params (ignored)
46 static int ClearPlaylist(const std::vector
<std::string
>& params
)
48 CServiceBroker::GetPlaylistPlayer().Clear();
53 /*! \brief Start a playlist from a given offset.
54 * \param params The parameters.
55 * \details params[0] = Position in playlist or playlist type.
56 * params[1] = Position in playlist if params[0] is playlist type (optional).
58 static int PlayOffset(const std::vector
<std::string
>& params
)
60 // playlist.playoffset(offset)
61 // playlist.playoffset(music|video,offset)
62 std::string strPos
= params
[0];
63 std::string
paramlow(params
[0]);
64 StringUtils::ToLower(paramlow
);
65 if (params
.size() > 1)
67 // ignore any other parameters if present
68 std::string strPlaylist
= params
[0];
71 int iPlaylist
= PLAYLIST_NONE
;
72 if (paramlow
== "music")
73 iPlaylist
= PLAYLIST_MUSIC
;
74 else if (paramlow
== "video")
75 iPlaylist
= PLAYLIST_VIDEO
;
78 if (iPlaylist
== PLAYLIST_NONE
)
80 CLog::Log(LOGERROR
, "Playlist.PlayOffset called with unknown playlist: {}", strPlaylist
);
84 // user wants to play the 'other' playlist
85 if (iPlaylist
!= CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist())
87 g_application
.StopPlaying();
88 CServiceBroker::GetPlaylistPlayer().Reset();
89 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(iPlaylist
);
92 // play the desired offset
93 int pos
= atol(strPos
.c_str());
94 // playlist is already playing
95 if (g_application
.GetAppPlayer().IsPlaying())
96 CServiceBroker::GetPlaylistPlayer().PlayNext(pos
);
97 // we start playing the 'other' playlist so we need to use play to initialize the player state
99 CServiceBroker::GetPlaylistPlayer().Play(pos
, "");
104 /*! \brief Control player.
105 * \param params The parameters
106 * \details params[0] = Control to execute.
107 * params[1] = "notify" to notify user (optional, certain controls).
109 static int PlayerControl(const std::vector
<std::string
>& params
)
111 g_application
.ResetScreenSaver();
112 g_application
.WakeUpScreenSaverAndDPMS();
114 std::string
paramlow(params
[0]);
115 StringUtils::ToLower(paramlow
);
117 if (paramlow
== "play")
119 // either resume playing, or pause
120 if (g_application
.GetAppPlayer().IsPlaying())
122 if (g_application
.GetAppPlayer().GetPlaySpeed() != 1)
123 g_application
.GetAppPlayer().SetPlaySpeed(1);
125 g_application
.GetAppPlayer().Pause();
128 else if (paramlow
== "stop")
130 g_application
.StopPlaying();
132 else if (StringUtils::StartsWithNoCase(params
[0], "frameadvance"))
134 std::string strFrames
;
135 if (params
[0].size() == 12)
136 CLog::Log(LOGERROR
, "PlayerControl(frameadvance(n)) called with no argument");
137 else if (params
[0].size() < 15) // arg must be at least "(N)"
138 CLog::Log(LOGERROR
, "PlayerControl(frameadvance(n)) called with invalid argument: \"{}\"",
139 params
[0].substr(13));
142 strFrames
= params
[0].substr(13);
143 StringUtils::TrimRight(strFrames
, ")");
144 float frames
= (float) atof(strFrames
.c_str());
145 g_application
.GetAppPlayer().FrameAdvance(frames
);
147 else if (paramlow
=="rewind" || paramlow
== "forward")
149 if (g_application
.GetAppPlayer().IsPlaying() && !g_application
.GetAppPlayer().IsPaused())
151 float playSpeed
= g_application
.GetAppPlayer().GetPlaySpeed();
153 if (paramlow
== "rewind" && playSpeed
== 1) // Enables Rewinding
155 else if (paramlow
== "rewind" && playSpeed
> 1) //goes down a notch if you're FFing
157 else if (paramlow
== "forward" && playSpeed
< 1) //goes up a notch if you're RWing
166 if (playSpeed
> 32 || playSpeed
< -32)
169 g_application
.GetAppPlayer().SetPlaySpeed(playSpeed
);
172 else if (paramlow
=="tempoup" || paramlow
== "tempodown")
174 if (g_application
.GetAppPlayer().SupportsTempo() &&
175 g_application
.GetAppPlayer().IsPlaying() && !g_application
.GetAppPlayer().IsPaused())
177 float playTempo
= g_application
.GetAppPlayer().GetPlayTempo();
178 if (paramlow
== "tempodown")
180 else if (paramlow
== "tempoup")
183 g_application
.GetAppPlayer().SetTempo(playTempo
);
186 else if (StringUtils::StartsWithNoCase(params
[0], "tempo"))
188 if (params
[0].size() == 5)
189 CLog::Log(LOGERROR
, "PlayerControl(tempo(n)) called with no argument");
190 else if (params
[0].size() < 8) // arg must be at least "(N)"
191 CLog::Log(LOGERROR
, "PlayerControl(tempo(n)) called with invalid argument: \"{}\"",
192 params
[0].substr(6));
195 CApplicationPlayer
& player
= g_application
.GetAppPlayer();
196 if (player
.SupportsTempo() && player
.IsPlaying() && !player
.IsPaused())
198 std::string strTempo
= params
[0].substr(6);
199 StringUtils::TrimRight(strTempo
, ")");
200 float playTempo
= strtof(strTempo
.c_str(), nullptr);
202 player
.SetTempo(playTempo
);
206 else if (paramlow
== "next")
208 g_application
.OnAction(CAction(ACTION_NEXT_ITEM
));
210 else if (paramlow
== "previous")
212 g_application
.OnAction(CAction(ACTION_PREV_ITEM
));
214 else if (paramlow
== "bigskipbackward")
216 if (g_application
.GetAppPlayer().IsPlaying())
217 g_application
.GetAppPlayer().Seek(false, true);
219 else if (paramlow
== "bigskipforward")
221 if (g_application
.GetAppPlayer().IsPlaying())
222 g_application
.GetAppPlayer().Seek(true, true);
224 else if (paramlow
== "smallskipbackward")
226 if (g_application
.GetAppPlayer().IsPlaying())
227 g_application
.GetAppPlayer().Seek(false, false);
229 else if (paramlow
== "smallskipforward")
231 if (g_application
.GetAppPlayer().IsPlaying())
232 g_application
.GetAppPlayer().Seek(true, false);
234 else if (StringUtils::StartsWithNoCase(params
[0], "seekpercentage"))
237 if (params
[0].size() == 14)
238 CLog::Log(LOGERROR
,"PlayerControl(seekpercentage(n)) called with no argument");
239 else if (params
[0].size() < 17) // arg must be at least "(N)"
240 CLog::Log(LOGERROR
, "PlayerControl(seekpercentage(n)) called with invalid argument: \"{}\"",
241 params
[0].substr(14));
244 // Don't bother checking the argument: an invalid arg will do seek(0)
245 offset
= params
[0].substr(15);
246 StringUtils::TrimRight(offset
, ")");
247 float offsetpercent
= (float) atof(offset
.c_str());
248 if (offsetpercent
< 0 || offsetpercent
> 100)
249 CLog::Log(LOGERROR
, "PlayerControl(seekpercentage(n)) argument, {:f}, must be 0-100",
251 else if (g_application
.GetAppPlayer().IsPlaying())
252 g_application
.SeekPercentage(offsetpercent
);
255 else if (paramlow
== "showvideomenu")
257 if( g_application
.GetAppPlayer().IsPlaying() )
258 g_application
.GetAppPlayer().OnAction(CAction(ACTION_SHOW_VIDEOMENU
));
260 else if (StringUtils::StartsWithNoCase(params
[0], "partymode"))
262 std::string strXspPath
;
263 //empty param=music, "music"=music, "video"=video, else xsp path
264 PartyModeContext context
= PARTYMODECONTEXT_MUSIC
;
265 if (params
[0].size() > 9)
267 if (params
[0].size() == 16 && StringUtils::EndsWithNoCase(params
[0], "video)"))
268 context
= PARTYMODECONTEXT_VIDEO
;
269 else if (params
[0].size() != 16 || !StringUtils::EndsWithNoCase(params
[0], "music)"))
271 strXspPath
= params
[0].substr(10);
272 StringUtils::TrimRight(strXspPath
, ")");
273 context
= PARTYMODECONTEXT_UNKNOWN
;
276 if (g_partyModeManager
.IsEnabled())
277 g_partyModeManager
.Disable();
279 g_partyModeManager
.Enable(context
, strXspPath
);
281 else if (paramlow
== "random" || paramlow
== "randomoff" || paramlow
== "randomon")
283 // get current playlist
284 int iPlaylist
= CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
286 // reverse the current setting
287 bool shuffled
= CServiceBroker::GetPlaylistPlayer().IsShuffled(iPlaylist
);
288 if ((shuffled
&& paramlow
== "randomon") || (!shuffled
&& paramlow
== "randomoff"))
291 // check to see if we should notify the user
292 bool notify
= (params
.size() == 2 && StringUtils::EqualsNoCase(params
[1], "notify"));
293 CServiceBroker::GetPlaylistPlayer().SetShuffle(iPlaylist
, !shuffled
, notify
);
295 // save settings for now playing windows
299 CMediaSettings::GetInstance().SetMusicPlaylistShuffled(CServiceBroker::GetPlaylistPlayer().IsShuffled(iPlaylist
));
300 CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
303 CMediaSettings::GetInstance().SetVideoPlaylistShuffled(CServiceBroker::GetPlaylistPlayer().IsShuffled(iPlaylist
));
304 CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
310 CGUIMessage
msg(GUI_MSG_PLAYLISTPLAYER_RANDOM
, 0, 0, iPlaylist
, CServiceBroker::GetPlaylistPlayer().IsShuffled(iPlaylist
));
311 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg
);
313 else if (StringUtils::StartsWithNoCase(params
[0], "repeat"))
315 // get current playlist
316 int iPlaylist
= CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
317 PLAYLIST::REPEAT_STATE previous_state
= CServiceBroker::GetPlaylistPlayer().GetRepeat(iPlaylist
);
319 std::string
paramlow(params
[0]);
320 StringUtils::ToLower(paramlow
);
322 PLAYLIST::REPEAT_STATE state
;
323 if (paramlow
== "repeatall")
324 state
= PLAYLIST::REPEAT_ALL
;
325 else if (paramlow
== "repeatone")
326 state
= PLAYLIST::REPEAT_ONE
;
327 else if (paramlow
== "repeatoff")
328 state
= PLAYLIST::REPEAT_NONE
;
329 else if (previous_state
== PLAYLIST::REPEAT_NONE
)
330 state
= PLAYLIST::REPEAT_ALL
;
331 else if (previous_state
== PLAYLIST::REPEAT_ALL
)
332 state
= PLAYLIST::REPEAT_ONE
;
334 state
= PLAYLIST::REPEAT_NONE
;
336 if (state
== previous_state
)
339 // check to see if we should notify the user
340 bool notify
= (params
.size() == 2 && StringUtils::EqualsNoCase(params
[1], "notify"));
341 CServiceBroker::GetPlaylistPlayer().SetRepeat(iPlaylist
, state
, notify
);
343 // save settings for now playing windows
347 CMediaSettings::GetInstance().SetMusicPlaylistRepeat(state
== PLAYLIST::REPEAT_ALL
);
348 CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
351 CMediaSettings::GetInstance().SetVideoPlaylistRepeat(state
== PLAYLIST::REPEAT_ALL
);
352 CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
355 // send messages so now playing window can get updated
356 CGUIMessage
msg(GUI_MSG_PLAYLISTPLAYER_REPEAT
, 0, 0, iPlaylist
, (int)state
);
357 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg
);
359 else if (StringUtils::StartsWithNoCase(params
[0], "resumelivetv"))
361 CFileItem
& fileItem(g_application
.CurrentFileItem());
362 std::shared_ptr
<PVR::CPVRChannel
> channel
= fileItem
.HasPVRRecordingInfoTag() ? fileItem
.GetPVRRecordingInfoTag()->Channel() : std::shared_ptr
<PVR::CPVRChannel
>();
366 const std::shared_ptr
<PVR::CPVRChannelGroupMember
> groupMember
=
367 CServiceBroker::GetPVRManager().GUIActions()->GetChannelGroupMember(channel
);
370 CLog::Log(LOGERROR
, "ResumeLiveTv could not obtain channel group member for channel: {}",
371 channel
->ChannelName());
375 CFileItem
playItem(groupMember
);
376 if (!g_application
.PlayMedia(playItem
, "", channel
->IsRadio() ? PLAYLIST_MUSIC
: PLAYLIST_VIDEO
))
378 CLog::Log(LOGERROR
, "ResumeLiveTv could not play channel: {}", channel
->ChannelName());
383 else if (paramlow
== "reset")
385 g_application
.OnAction(CAction(ACTION_PLAYER_RESET
));
391 /*! \brief Play currently inserted DVD.
392 * \param params The parameters.
393 * \details params[0] = "restart" to restart from resume point (optional).
395 static int PlayDVD(const std::vector
<std::string
>& params
)
398 bool restart
= false;
399 if (!params
.empty() && StringUtils::EqualsNoCase(params
[0], "restart"))
401 MEDIA_DETECT::CAutorun::PlayDisc(CServiceBroker::GetMediaManager().GetDiscPath(), true, restart
);
407 /*! \brief Start playback of media.
408 * \param params The parameters.
409 * \details params[0] = URL to media to play (optional).
410 * params[1,...] = "isdir" if media is a directory (optional).
411 * params[1,...] = "1" to start playback in fullscreen (optional).
412 * params[1,...] = "resume" to force resuming (optional).
413 * params[1,...] = "noresume" to force not resuming (optional).
414 * params[1,...] = "playoffset=<offset>" to start playback from a given position in a playlist (optional).
416 static int PlayMedia(const std::vector
<std::string
>& params
)
418 CFileItem
item(params
[0], false);
419 if (URIUtils::HasSlashAtEnd(params
[0]))
420 item
.m_bIsFolder
= true;
422 // restore to previous window if needed
423 if( CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW
||
424 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO
||
425 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME
||
426 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION
)
427 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
430 g_application
.ResetScreenSaver();
431 g_application
.WakeUpScreenSaverAndDPMS();
433 // ask if we need to check guisettings to resume
434 bool askToResume
= true;
436 for (unsigned int i
= 1 ; i
< params
.size() ; i
++)
438 if (StringUtils::EqualsNoCase(params
[i
], "isdir"))
439 item
.m_bIsFolder
= true;
440 else if (params
[i
] == "1") // set fullscreen or windowed
441 CMediaSettings::GetInstance().SetMediaStartWindowed(true);
442 else if (StringUtils::EqualsNoCase(params
[i
], "resume"))
444 // force the item to resume (if applicable) (see CApplication::PlayMedia)
445 item
.SetStartOffset(STARTOFFSET_RESUME
);
448 else if (StringUtils::EqualsNoCase(params
[i
], "noresume"))
450 // force the item to start at the beginning (m_lStartOffset is initialized to 0)
453 else if (StringUtils::StartsWithNoCase(params
[i
], "playoffset=")) {
454 playOffset
= atoi(params
[i
].substr(11).c_str()) - 1;
455 item
.SetProperty("playlist_starting_track", playOffset
);
459 if (!item
.m_bIsFolder
&& item
.IsPlugin())
460 item
.SetProperty("IsPlayable", true);
462 if ( askToResume
== true )
464 if ( CGUIWindowVideoBase::ShowResumeMenu(item
) == false )
467 if (item
.m_bIsFolder
|| item
.IsPlayList())
470 std::string extensions
= CServiceBroker::GetFileExtensionProvider().GetVideoExtensions() + "|" + CServiceBroker::GetFileExtensionProvider().GetMusicExtensions();
471 if (item
.IsPlayList())
472 CUtil::GetRecursiveListing(item
.GetPath(), items
, extensions
, XFILE::DIR_FLAG_DEFAULTS
);
474 XFILE::CDirectory::GetDirectory(item
.GetPath(), items
, extensions
, XFILE::DIR_FLAG_DEFAULTS
);
476 if (!items
.IsEmpty()) // fall through on non expandable playlist
478 bool containsMusic
= false, containsVideo
= false;
479 for (int i
= 0; i
< items
.Size(); i
++)
481 bool isVideo
= items
[i
]->IsVideo();
482 containsMusic
|= !isVideo
;
483 containsVideo
|= isVideo
;
485 if (containsMusic
&& containsVideo
)
489 std::unique_ptr
<CGUIViewState
> state(CGUIViewState::GetViewState(containsVideo
? WINDOW_VIDEO_NAV
: WINDOW_MUSIC_NAV
, items
));
491 items
.Sort(state
->GetSortMethod());
493 items
.Sort(SortByLabel
, SortOrderAscending
);
495 int playlist
= containsVideo
? PLAYLIST_VIDEO
: PLAYLIST_MUSIC
;
496 // Mixed playlist item played by music player, mixed content folder has music removed
497 if (containsMusic
&& containsVideo
)
499 if (item
.IsPlayList())
500 playlist
= PLAYLIST_MUSIC
;
503 for (int i
= items
.Size() - 1; i
>= 0; i
--) //remove music entries
505 if (!items
[i
]->IsVideo())
511 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlist
);
512 CServiceBroker::GetPlaylistPlayer().Add(playlist
, items
);
513 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlist
);
514 CServiceBroker::GetPlaylistPlayer().Play(playOffset
, "");
518 if ((item
.IsAudio() || item
.IsVideo()) && !item
.IsSmartPlayList())
519 CServiceBroker::GetPlaylistPlayer().Play(std::make_shared
<CFileItem
>(item
), "");
521 g_application
.PlayMedia(item
, "", PLAYLIST_NONE
);
526 /*! \brief Start playback with a given playback core.
527 * \param params The parameters.
528 * \details params[0] = Name of playback core.
530 static int PlayWith(const std::vector
<std::string
>& params
)
532 g_application
.OnAction(CAction(ACTION_PLAYER_PLAY
, params
[0]));
537 /*! \brief Seek in currently playing media.
538 * \param params The parameters.
539 * \details params[0] = Number of seconds to seek.
541 static int Seek(const std::vector
<std::string
>& params
)
543 if (g_application
.GetAppPlayer().IsPlaying())
544 g_application
.GetAppPlayer().GetSeekHandler().SeekSeconds(atoi(params
[0].c_str()));
549 static int SubtitleShiftUp(const std::vector
<std::string
>& params
)
551 CAction action
{ACTION_SUBTITLE_VSHIFT_UP
};
552 if (!params
.empty() && params
[0] == "save")
553 action
.SetText("save");
554 CPlayerController::GetInstance().OnAction(action
);
558 static int SubtitleShiftDown(const std::vector
<std::string
>& params
)
560 CAction action
{ACTION_SUBTITLE_VSHIFT_DOWN
};
561 if (!params
.empty() && params
[0] == "save")
562 action
.SetText("save");
563 CPlayerController::GetInstance().OnAction(action
);
567 // Note: For new Texts with comma add a "\" before!!! Is used for table text.
569 /// \page page_List_of_built_in_functions
570 /// \section built_in_functions_12 Player built-in's
572 /// -----------------------------------------------------------------------------
579 /// <b>`PlaysDisc(parm)`</b>\n
580 /// <b>`PlayDVD(param)`</b>(deprecated)
582 /// Plays the inserted disc\, like CD\, DVD or Blu-ray\, in the disc drive.
583 /// @param[in] param "restart" to restart from resume point (optional)
586 /// <b>`PlayerControl(control[\,param])`</b>
588 /// Allows control of music and videos. <br>
590 /// | Control | Video playback behaviour | Audio playback behaviour | Added in |
591 /// |:------------------------|:---------------------------------------|:----------------------------|:------------|
592 /// | Play | Play/Pause | Play/Pause | |
593 /// | Stop | Stop | Stop | |
594 /// | Forward | Fast Forward | Fast Forward | |
595 /// | Rewind | Rewind | Rewind | |
596 /// | Next | Next chapter or movie in playlists | Next track | |
597 /// | Previous | Previous chapter or movie in playlists | Previous track | |
598 /// | TempoUp | Increases playback speed | none | Kodi v18 |
599 /// | TempoDown | Decreases playback speed | none | Kodi v18 |
600 /// | Tempo(n) | Sets playback speed to given value | none | Kodi v19 |
601 /// | BigSkipForward | Big Skip Forward | Big Skip Forward | Kodi v15 |
602 /// | BigSkipBackward | Big Skip Backward | Big Skip Backward | Kodi v15 |
603 /// | SmallSkipForward | Small Skip Forward | Small Skip Forward | Kodi v15 |
604 /// | SmallSkipBackward | Small Skip Backward | Small Skip Backward | Kodi v15 |
605 /// | SeekPercentage(n) | Seeks to given percentage | Seeks to given percentage | |
606 /// | Random * | Toggle Random Playback | Toggle Random Playback | |
607 /// | RandomOn | Sets 'Random' to 'on' | Sets 'Random' to 'on' | |
608 /// | RandomOff | Sets 'Random' to 'off' | Sets 'Random' to 'off' | |
609 /// | Repeat * | Cycles through repeat modes | Cycles through repeat modes | |
610 /// | RepeatOne | Repeats a single video | Repeats a single track | |
611 /// | RepeatAll | Repeat all videos in a list | Repeats all tracks in a list| |
612 /// | RepeatOff | Sets 'Repeat' to 'off' | Sets 'Repeat' to 'off' | |
613 /// | Partymode(music) ** | none | Toggles music partymode | |
614 /// | Partymode(video) ** | Toggles video partymode | none | |
615 /// | Partymode(path to .xsp) | Partymode for *.xsp-file | Partymode for *.xsp-file | |
616 /// | ShowVideoMenu | Shows the DVD/BR menu if available | none | |
617 /// | FrameAdvance(n) *** | Advance video by _n_ frames | none | Kodi v18 |
618 /// | SubtitleShiftUp(save) | Shift up the subtitle position, add "save" to save the change permanently | none | Kodi v20 |
619 /// | SubtitleShiftDown(save) | Shift down the subtitle position, add "save" to save the change permanently | none | Kodi v20 |
621 /// '*' = For these controls\, the PlayerControl built-in function can make use of the 'notify'-parameter. For example: PlayerControl(random\, notify)
623 /// '**' = If no argument is given for 'partymode'\, the control will default to music.
625 /// '***' = This only works if the player is paused.
627 /// @param[in] control Control to execute.
628 /// @param[in] param "notify" to notify user (optional\, certain controls).
630 /// @note 'TempoUp' or 'TempoDown' only works if "Sync playback to display" is enabled.
631 /// @note 'Next' will behave differently while using video playlists. In those\, chapters will be ignored and the next movie will be played.
634 /// <b>`Playlist.Clear`</b>
636 /// Clear the current playlist
637 /// @param[in] (ignored)
640 /// <b>`Playlist.PlayOffset(positionType[\,position])`</b>
642 /// Start playing from a particular offset in the playlist
643 /// @param[in] positionType Position in playlist or playlist type.
644 /// @param[in] position Position in playlist if params[0] is playlist type (optional).
647 /// <b>`PlayMedia(media[\,isdir][\,1]\,[playoffset=xx])`</b>
649 /// Plays the media. This can be a playlist\, music\, or video file\, directory\,
650 /// plugin or an Url. The optional parameter "\,isdir" can be used for playing
651 /// a directory. "\,1" will start the media without switching to fullscreen.
652 /// If media is a playlist\, you can use playoffset=xx where xx is
653 /// the position to start playback from.
654 /// @param[in] media URL to media to play (optional).
655 /// @param[in] isdir Set "isdir" if media is a directory (optional).
656 /// @param[in] windowed Set "1" to start playback without switching to fullscreen (optional).
657 /// @param[in] resume Set "resume" to force resuming (optional).
658 /// @param[in] noresume Set "noresume" to force not resuming (optional).
659 /// @param[in] playeroffset Set "playoffset=<offset>" to start playback from a given position in a playlist (optional).
662 /// <b>`PlayWith(core)`</b>
664 /// Play the selected item with the specified player core.
665 /// @param[in] core Name of playback core.
668 /// <b>`Seek(seconds)`</b>
670 /// Seeks to the specified relative amount of seconds within the current
671 /// playing media. A negative value will seek backward and a positive value forward.
672 /// @param[in] seconds Number of seconds to seek.
678 CBuiltins::CommandMap
CPlayerBuiltins::GetOperations() const
681 {"playdisc", {"Plays the inserted disc, like CD, DVD or Blu-ray, in the disc drive.", 0, PlayDVD
}},
682 {"playdvd", {"Plays the inserted disc, like CD, DVD or Blu-ray, in the disc drive.", 0, PlayDVD
}},
683 {"playlist.clear", {"Clear the current playlist", 0, ClearPlaylist
}},
684 {"playlist.playoffset", {"Start playing from a particular offset in the playlist", 1, PlayOffset
}},
685 {"playercontrol", {"Control the music or video player", 1, PlayerControl
}},
686 {"playmedia", {"Play the specified media file (or playlist)", 1, PlayMedia
}},
687 {"playwith", {"Play the selected item with the specified core", 1, PlayWith
}},
688 {"seek", {"Performs a seek in seconds on the current playing media file", 1, Seek
}},
689 {"subtitleshiftup", {"Shift up the subtitle position", 0, SubtitleShiftUp
}},
690 {"subtitleshiftdown", {"Shift down the subtitle position", 0, SubtitleShiftDown
}},