~music-app-dev/music-app/trunk

870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
1
/*
870.1.65 by Andrew Hayzen
* Update copyright dates
2
 * Copyright (C) 2015, 2016
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
3
 *      Andrew Hayzen <ahayzen@gmail.com>
4
 *      Victor Thompson <victor.thompson@gmail.com>
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; version 3.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
870.3.1 by Andrew Hayzen
* Bump QtMultimedia import to 5.6 and remove unused ones
19
import QtMultimedia 5.6
870.1.2 by Andrew Hayzen
* Merge of trunk
20
import QtQuick 2.4
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
21
import Qt.labs.settings 1.0
22
870.1.13 by Andrew Hayzen
* Re-add storing of the Queue in a db for now until save()/load() work
23
import QtQuick.LocalStorage 2.0
24
import "../logic/meta-database.js" as Library
25
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
26
Item {
27
    objectName: "player"
28
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
29
    // For autopilot as we can't access the MediaPlayer object pad.lv/1269578
30
    readonly property bool isPlaying: mediaPlayerObject.playbackState === MediaPlayer.PlayingState
870.1.26 by Andrew Hayzen
* Various fixes for autopilot
31
    readonly property alias count: mediaPlayerPlaylist.itemCount
32
    readonly property alias currentIndex: mediaPlayerPlaylist.currentIndex
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
33
    readonly property alias currentItemSource: mediaPlayerPlaylist.currentItemSource
34
    readonly property alias position: mediaPlayerObject.position
35
36
    // FIXME: pad.lv/1269578 use Item as autopilot cannot 'see' a var/QtObject
37
    property alias currentMeta: currentMetaItem
38
39
    property alias mediaPlayer: mediaPlayerObject
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
40
    property alias repeat: settings.repeat
41
    property alias shuffle: settings.shuffle
42
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
43
    Item {
44
        id: currentMetaItem
45
        objectName: "currentMeta"
46
47
        property string album: ""
48
        property string art: ""
49
        property string author: ""
50
        property string filename: ""
51
        property string title: ""
991.2.1 by Victor Thompson
* Fix fallback art when the it is linked to the Player's currentMeta
52
        property bool useFallbackArt: false
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
53
    }
54
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
55
    // Return the metadata for the source given from mediascanner2
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
56
    function metaForSource(source) {
57
        var blankMeta = {
58
            album: "",
59
            art: "",
60
            author: "",
61
            filename: "",
62
            title: ""
63
        };
64
65
        source = source.toString();
66
67
        if (source.indexOf("file://") === 0) {
68
            source = source.substring(7);
69
        }
70
870.1.2 by Andrew Hayzen
* Merge of trunk
71
        return musicStore.lookup(decodeFileURI(source)) || blankMeta;
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
72
    }
73
74
    Settings {
75
        id: settings
76
        category: "PlayerSettings"
77
78
        property bool repeat: true
79
        property bool shuffle: false
80
    }
81
82
    MediaPlayer {
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
83
        id: mediaPlayerObject
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
84
        playlist: Playlist {
85
            id: mediaPlayerPlaylist
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
86
            playbackMode: {
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
87
                if (settings.shuffle) {
88
                    Playlist.Random
89
                } else if (settings.repeat) {
90
                    Playlist.Loop
91
                } else {
92
                    Playlist.Sequential
93
                }
94
            }
95
870.1.36 by Andrew Hayzen
* Check canGoPrevious and canGoNext in all instances where previous and next are used
96
            // as that doesn't emit changes
870.1.38 by Andrew Hayzen
* Colour next/previous etc button pressed
97
            readonly property bool canGoPrevious: {  // FIXME: pad.lv/1517580 use previousIndex() > -1 after mh implements it
870.1.36 by Andrew Hayzen
* Check canGoPrevious and canGoNext in all instances where previous and next are used
98
                currentIndex !== 0 ||
99
                settings.repeat ||
870.1.53 by Andrew Hayzen
* Fix typos and bump framework to 15.04.3-qml
100
                settings.shuffle ||  // FIXME: pad.lv/1517580 no way to know when we are at the end of a shuffle yet
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
101
                mediaPlayerObject.position > 5000
870.1.36 by Andrew Hayzen
* Check canGoPrevious and canGoNext in all instances where previous and next are used
102
            }
870.1.38 by Andrew Hayzen
* Colour next/previous etc button pressed
103
            readonly property bool canGoNext: {  // FIXME: pad.lv/1517580 use nextIndex() > -1 after mh implements it
870.1.36 by Andrew Hayzen
* Check canGoPrevious and canGoNext in all instances where previous and next are used
104
                currentIndex !== (itemCount - 1) ||
105
                settings.repeat ||
870.1.53 by Andrew Hayzen
* Fix typos and bump framework to 15.04.3-qml
106
                settings.shuffle  // FIXME: pad.lv/1517580 no way to know when we are at the end of a shuffle yet
870.1.36 by Andrew Hayzen
* Check canGoPrevious and canGoNext in all instances where previous and next are used
107
            }
108
            readonly property int count: itemCount  // header actions etc depend on the model having 'count'
870.1.23 by Andrew Hayzen
* Add .empty property to MediaPlayer.Playlist
109
            readonly property bool empty: itemCount === 0
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
110
            property int pendingCurrentIndex: -1
111
            property var pendingCurrentState: null
870.1.17 by Andrew Hayzen
* Implement option to defer a shuffle until a model count is reached
112
            property int pendingShuffle: -1
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
113
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
114
            onCurrentItemSourceChanged: {
115
                var meta = metaForSource(currentItemSource);
116
117
                currentMeta.album = meta.album;
118
                currentMeta.art = meta.art;
119
                currentMeta.author = meta.author;
120
                currentMeta.filename = meta.filename;
121
                currentMeta.title = meta.title;
991.2.1 by Victor Thompson
* Fix fallback art when the it is linked to the Player's currentMeta
122
                currentMeta.useFallbackArt = false;
870.1.54 by Andrew Hayzen
* Workaround for bug 1494031 by querying gst for the position and duration after the source has been set
123
124
                mediaPlayerObject._calcProgress();
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
125
            }
870.2.3 by Jim Hodapp
More changes for new QDeclarativePlaylist API.
126
            onItemChanged: {
870.2.16 by Jim Hodapp
Updated play queue saving debug statements
127
                console.debug("*** Saving play queue in onItemChanged");
870.1.3 by Andrew Hayzen
* Workaround for onCurrentSourceChanged not being called when appending and possibly remove/changing
128
                saveQueue()
129
            }
870.2.2 by Jim Hodapp
More changes for new QDeclarativePlaylist API.
130
            onItemInserted: {
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
131
                // When add to queue is done on an empty list currentIndex needs to be set
870.1.17 by Andrew Hayzen
* Implement option to defer a shuffle until a model count is reached
132
                if (start === 0 && currentIndex === -1 && pendingCurrentIndex < 1 && pendingShuffle === -1) {
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
133
                    currentIndex = 0;
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
134
135
                    pendingCurrentIndex = -1;
136
                    processPendingCurrentState();
137
                }
138
139
                // Check if the pendingCurrentIndex is now valid
870.2.8 by Jim Hodapp
More changes for new QDeclarativePlaylist API.
140
                if (pendingCurrentIndex !== -1 && pendingCurrentIndex < itemCount) {
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
141
                    currentIndex = pendingCurrentIndex;
142
143
                    pendingCurrentIndex = -1;
144
                    processPendingCurrentState();
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
145
                }
146
870.1.17 by Andrew Hayzen
* Implement option to defer a shuffle until a model count is reached
147
                // Check if there is pending shuffle
148
                // pendingShuffle holds the expected size of the model
870.2.17 by Jim Hodapp
Make sure save queue and restore queue work
149
                if (pendingShuffle > -1 && pendingShuffle <= itemCount) {
870.1.17 by Andrew Hayzen
* Implement option to defer a shuffle until a model count is reached
150
                    pendingShuffle = -1;
870.1.36 by Andrew Hayzen
* Check canGoPrevious and canGoNext in all instances where previous and next are used
151
870.1.46 by Andrew Hayzen
* Various fixes from code review
152
                    nextWrapper();  // find a random track
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
153
                    mediaPlayerObject.play();  // next does not enforce play
870.1.17 by Andrew Hayzen
* Implement option to defer a shuffle until a model count is reached
154
                }
155
870.2.16 by Jim Hodapp
Updated play queue saving debug statements
156
                console.debug("*** Saving play queue in onItemInserted");
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
157
                saveQueue()
870.1.3 by Andrew Hayzen
* Workaround for onCurrentSourceChanged not being called when appending and possibly remove/changing
158
            }
870.2.2 by Jim Hodapp
More changes for new QDeclarativePlaylist API.
159
            onItemRemoved: {
870.2.16 by Jim Hodapp
Updated play queue saving debug statements
160
                console.debug("*** Saving play queue in onItemRemoved");
870.1.3 by Andrew Hayzen
* Workaround for onCurrentSourceChanged not being called when appending and possibly remove/changing
161
                saveQueue()
162
            }
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
163
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
164
            function addItemsFromModel(model) {
165
                var items = []
870.1.12 by Andrew Hayzen
* Provisional change to use addSources instead of addSource
166
870.1.67 by Andrew Hayzen
* Fix for Shuffle All and Queue All not working with playlists due to them using LibraryListModel
167
                // TODO: remove once playlists uses U1DB
168
                if (model.hasOwnProperty("linkLibraryListModel")) {
169
                    model = model.linkLibraryListModel;
170
                }
171
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
172
                for (var i=0; i < model.rowCount; i++) {
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
173
                    items.push(Qt.resolvedUrl(model.get(i, model.RoleModelData).filename));
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
174
                }
870.1.12 by Andrew Hayzen
* Provisional change to use addSources instead of addSource
175
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
176
                addItems(items);
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
177
            }
178
870.1.37 by Andrew Hayzen
* Wrap clear method to ensure stop is called first
179
            // Wrap the clear() method because we need to call stop first
870.1.43 by Andrew Hayzen
* Use the new removeItems(start, end) and rename clear_wrapper to clearWrapper
180
            function clearWrapper() {
870.1.37 by Andrew Hayzen
* Wrap clear method to ensure stop is called first
181
                // Stop the current playback (this ensures that play is run later)
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
182
                if (mediaPlayerObject.playbackState === MediaPlayer.PlayingState) {
183
                    mediaPlayerObject.stop();
870.1.37 by Andrew Hayzen
* Wrap clear method to ensure stop is called first
184
                }
185
186
                clear();
187
            }
188
870.1.47 by Andrew Hayzen
* Various fixes from code review and extra code commentary
189
            // Replicates a model.get() on a ms2 model
190
            function get(index, role) {
191
                return metaForSource(itemSource(index));
192
            }
193
870.1.46 by Andrew Hayzen
* Various fixes from code review
194
            // Wrap the next() method so we can check canGoNext
195
            function nextWrapper() {
196
                if (canGoNext) {
197
                    next();
198
                }
199
            }
200
201
            // Wrap the previous() method so we can check canGoPrevious
202
            function previousWrapper() {
203
                if (canGoPrevious) {
204
                    previous();
205
                }
206
            }
207
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
208
            // Process the pending current PlaybackState
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
209
            function processPendingCurrentState() {
210
                if (pendingCurrentState === MediaPlayer.PlayingState) {
211
                    console.debug("Loading pending state play()");
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
212
                    mediaPlayerObject.play();
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
213
                } else if (pendingCurrentState === MediaPlayer.PausedState) {
214
                    console.debug("Loading pending state pause()");
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
215
                    mediaPlayerObject.pause();
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
216
                } else if (pendingCurrentState === MediaPlayer.StoppedState) {
217
                    console.debug("Loading pending state stop()");
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
218
                    mediaPlayerObject.stop();
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
219
                }
220
221
                pendingCurrentState = null;
222
            }
223
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
224
            // Wrapper for removeItems(from, to) so that we can use removeItems(list) until it is implemented upstream
870.1.43 by Andrew Hayzen
* Use the new removeItems(start, end) and rename clear_wrapper to clearWrapper
225
            function removeItemsWrapper(items) {
226
                var previous = -1, end = -1;
227
228
                // Sort indexes backwards so we don't have to deal with offsets when removing
229
                items.sort(function(a,b) { return b-a; });
230
870.1.47 by Andrew Hayzen
* Various fixes from code review and extra code commentary
231
                console.debug("To Remove", JSON.stringify(items));
870.1.43 by Andrew Hayzen
* Use the new removeItems(start, end) and rename clear_wrapper to clearWrapper
232
233
                // Merge ranges of indexes into sets of start, end points
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
234
                // and call removeItems as we go along
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
235
                for (var i=0; i < items.length; i++) {
870.1.43 by Andrew Hayzen
* Use the new removeItems(start, end) and rename clear_wrapper to clearWrapper
236
                    if (end == -1) {  // first value found set to first
237
                        end = items[i];
238
                    } else if (previous - 1 !== items[i]) {  // set has ended (next is not 1 lower)
870.1.47 by Andrew Hayzen
* Various fixes from code review and extra code commentary
239
                        console.debug("RemoveItems", previous, end);
870.1.51 by Andrew Hayzen
* Rename NewPlayer.qml to Player.qml as old player has now been totally removed
240
                        player.mediaPlayer.playlist.removeItems(previous, end);
870.1.43 by Andrew Hayzen
* Use the new removeItems(start, end) and rename clear_wrapper to clearWrapper
241
242
                        end = items[i];  // set new high value for the next set
243
                    }
244
245
                    previous = items[i];  // last value to check if next is 1 lower
246
                }
247
248
                // Remove last set in list as well
249
                if (items.length > 0) {
870.1.47 by Andrew Hayzen
* Various fixes from code review and extra code commentary
250
                    console.debug("RemoveItems", items[items.length - 1], end);
870.1.51 by Andrew Hayzen
* Rename NewPlayer.qml to Player.qml as old player has now been totally removed
251
                    player.mediaPlayer.playlist.removeItems(items[items.length - 1], end);
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
252
                }
253
            }
254
255
            function saveQueue(start, end) {
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
256
                // FIXME: load and save do not work yet pad.lv/1510225
257
                // so use our localstorage method for now
870.1.11 by Andrew Hayzen
* Disable save() call in saveQueue() for now
258
                // save("/home/phablet/.local/share/com.ubuntu.music/queue.m3u");
870.1.13 by Andrew Hayzen
* Re-add storing of the Queue in a db for now until save()/load() work
259
                if (mainView.loadedUI) {
870.1.48 by Andrew Hayzen
* Fixes to code commentary in NewPlayer.qml
260
                    // Don't be intelligent, just clear and rebuild the queue for now
870.1.13 by Andrew Hayzen
* Re-add storing of the Queue in a db for now until save()/load() work
261
                    Library.clearQueue();
262
263
                    var sources = [];
264
870.2.17 by Jim Hodapp
Make sure save queue and restore queue work
265
                    for (var i=0; i < mediaPlayerPlaylist.itemCount; i++) {
266
                        sources.push(mediaPlayerPlaylist.itemSource(i));
870.1.13 by Andrew Hayzen
* Re-add storing of the Queue in a db for now until save()/load() work
267
                    }
268
870.1.18 by Andrew Hayzen
* Extra protection against no tracks
269
                    if (sources.length > 0) {
270
                        Library.addQueueList(sources);
271
                    }
870.1.13 by Andrew Hayzen
* Re-add storing of the Queue in a db for now until save()/load() work
272
                }
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
273
            }
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
274
275
            function setCurrentIndex(index) {
870.2.17 by Jim Hodapp
Make sure save queue and restore queue work
276
                // Set the currentIndex but if the itemCount is too low then wait
277
                if (index < mediaPlayerPlaylist.itemCount) {
870.1.16 by Andrew Hayzen
* Add ability to defer setting the currentIndex and playbackState until the mediaCount is correct
278
                    mediaPlayerPlaylist.currentIndex = index;
279
                } else {
280
                    pendingCurrentIndex = index;
281
                }
282
            }
283
284
            function setPendingCurrentState(pendingState) {
285
                // Set the PlaybackState to set once pendingCurrentIndex is set
286
                pendingCurrentState = pendingState;
287
288
                if (pendingCurrentIndex === -1) {
289
                    processPendingCurrentState();
290
                }
291
            }
870.1.17 by Andrew Hayzen
* Implement option to defer a shuffle until a model count is reached
292
293
            function setPendingShuffle(modelSize) {
870.1.37 by Andrew Hayzen
* Wrap clear method to ensure stop is called first
294
                // Run next() and play() when the modelSize is reached
870.2.17 by Jim Hodapp
Make sure save queue and restore queue work
295
                if (modelSize <= itemCount) {
870.1.46 by Andrew Hayzen
* Various fixes from code review
296
                    mediaPlayerPlaylist.nextWrapper();  // find a random track
870.1.52 by Andrew Hayzen
* Use Item for currentMeta as var cannot be 'seen' by autopilot and add aliases for others that cannot be reached
297
                    mediaPlayerObject.play();  // next does not enforce play
870.1.17 by Andrew Hayzen
* Implement option to defer a shuffle until a model count is reached
298
                } else {
299
                    pendingShuffle = modelSize;
300
                }
301
            }
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
302
        }
303
870.1.56 by Andrew Hayzen
* When entering the stop state, if there is other media in the playlist instead enter the pause state so that position and duration can be retrieved
304
        property bool endOfMedia: false
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
305
        property double progress: 0
306
307
        onDurationChanged: _calcProgress()
308
        onPositionChanged: _calcProgress()
309
870.1.8 by Andrew Hayzen
* Connect EndOfMedia state
310
        onStatusChanged: {
870.1.41 by Andrew Hayzen
* Do not stop when repeat is enabled on EndOfMedia
311
            if (status == MediaPlayer.EndOfMedia && !settings.repeat) {
870.1.8 by Andrew Hayzen
* Connect EndOfMedia state
312
                console.debug("End of media, stopping.")
870.1.56 by Andrew Hayzen
* When entering the stop state, if there is other media in the playlist instead enter the pause state so that position and duration can be retrieved
313
314
                // Tells the onStopped to set the curentIndex = 0
315
                endOfMedia = true;
316
870.1.50 by Andrew Hayzen
* Fix for currentIndex becoming 1 not 0 after stopping from EndOfMedia
317
                stop();
870.1.8 by Andrew Hayzen
* Connect EndOfMedia state
318
            }
319
        }
320
870.1.9 by Andrew Hayzen
* Detect onStopped signal
321
        onStopped: {  // hit when pressing next() on last track with repeat off
322
            console.debug("onStopped.")
870.1.56 by Andrew Hayzen
* When entering the stop state, if there is other media in the playlist instead enter the pause state so that position and duration can be retrieved
323
324
            // FIXME: Workaround for pad.lv/1494031 in the stopped state
325
            // we do not get position/duration info so if there are items in
326
            // the queue and we have stopped instead pause
327
            if (playlist.itemCount > 0) {
328
                // We have just ended media so jump to start of playlist
329
                if (endOfMedia) {
330
                    playlist.currentIndex = 0;
331
332
                    // Play then pause otherwise when we come from EndOfMedia
333
                    // if calls next() until EndOfMedia again
334
                    play();
335
                }
336
337
                pause();
338
            }
339
340
            endOfMedia = false;  // always reset endOfMedia
870.1.9 by Andrew Hayzen
* Detect onStopped signal
341
            _calcProgress();  // ensures progress bar has reset
342
        }
343
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
344
        function _calcProgress() {
345
            if (duration > 0) {
346
                progress = position / duration;
870.1.8 by Andrew Hayzen
* Connect EndOfMedia state
347
            } else if (position >= duration) {
348
                progress = 0;
870.1.1 by Andrew Hayzen
* Initial port to new MediaPlayer object with playlist support
349
            } else {
350
                progress = 0;
351
            }
352
        }
353
354
        function toggle() {
355
            if (playbackState === MediaPlayer.PlayingState) {
356
                pause();
357
            } else {
358
                play();
359
            }
360
        }
361
    }
362
}