Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(244)

Side by Side Diff: chrome/browser/resources/file_manager/js/media/audio_player.js

Issue 10917188: Make Chrome OS Audio Player survive page reload and OS restart. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Removed debug trace Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 document.addEventListener('DOMContentLoaded', function() { 5 document.addEventListener('DOMContentLoaded', function() {
6 // Test harness sets the search string to prevent the automatic load. 6 // Test harness sets the search string to prevent the automatic load.
7 // It calls AudioPlayer.load() explicitly after initializing 7 // It calls AudioPlayer.load() explicitly after initializing
8 // the |chrome| variable with an appropriate mock object. 8 // the |chrome| variable with an appropriate mock object.
9 if (!document.location.search) { 9 if (!document.location.search) {
10 AudioPlayer.load(); 10 AudioPlayer.load();
(...skipping 25 matching lines...) Expand all
36 // but it would make keeping the list scroll position very tricky. 36 // but it would make keeping the list scroll position very tricky.
37 this.trackList_ = createChild('track-list'); 37 this.trackList_ = createChild('track-list');
38 this.trackStack_ = createChild('track-stack'); 38 this.trackStack_ = createChild('track-stack');
39 39
40 createChild('title-button close').addEventListener( 40 createChild('title-button close').addEventListener(
41 'click', function() { chrome.mediaPlayerPrivate.closeWindow() }); 41 'click', function() { chrome.mediaPlayerPrivate.closeWindow() });
42 42
43 createChild('title-button collapse').addEventListener( 43 createChild('title-button collapse').addEventListener(
44 'click', this.onExpandCollapse_.bind(this)); 44 'click', this.onExpandCollapse_.bind(this));
45 45
46 this.audioControls_ = new AudioControls( 46 this.audioControls_ = new FullWindowAudioControls(
47 createChild(), this.advance_.bind(this), this.onError_.bind(this)); 47 createChild(), this.advance_.bind(this), this.onError_.bind(this));
48 48
49 this.audioControls_.attachMedia(createChild('', 'audio')); 49 this.audioControls_.attachMedia(createChild('', 'audio'));
50 50
51 chrome.fileBrowserPrivate.getStrings(function(strings) { 51 chrome.fileBrowserPrivate.getStrings(function(strings) {
52 container.ownerDocument.title = strings['AUDIO_PLAYER_TITLE']; 52 container.ownerDocument.title = strings['AUDIO_PLAYER_TITLE'];
53 this.errorString_ = strings['AUDIO_ERROR']; 53 this.errorString_ = strings['AUDIO_ERROR'];
54 this.offlineString_ = strings['AUDIO_OFFLINE']; 54 this.offlineString_ = strings['AUDIO_OFFLINE'];
55 AudioPlayer.TrackInfo.DEFAULT_ARTIST = 55 AudioPlayer.TrackInfo.DEFAULT_ARTIST =
56 strings['AUDIO_PLAYER_DEFAULT_ARTIST']; 56 strings['AUDIO_PLAYER_DEFAULT_ARTIST'];
57 }.bind(this)); 57 }.bind(this));
58 } 58 }
59 59
60 /** 60 /**
61 * Key in the local storage for the list of track urls.
62 */
63 AudioPlayer.PLAYLIST_KEY = 'audioPlaylist';
64
65 /**
66 * Key in the local storage for the number of the current track.
67 */
68 AudioPlayer.TRACK_KEY = 'audioTrack';
69
70 /**
61 * Initial load method (static). 71 * Initial load method (static).
62 */ 72 */
63 AudioPlayer.load = function() { 73 AudioPlayer.load = function() {
64 document.ondragstart = function(e) { e.preventDefault() }; 74 document.ondragstart = function(e) { e.preventDefault() };
65 document.oncontextmenu = function(e) { e.preventDefault(); }; 75 document.oncontextmenu = function(e) { e.preventDefault(); };
66 76
67 // If the audio player is starting before the first instance of the File 77 // If the audio player is starting before the first instance of the File
68 // Manager then it does not have access to filesystem URLs. Request it now. 78 // Manager then it does not have access to filesystem URLs. Request it now.
69 chrome.fileBrowserPrivate.requestLocalFileSystem(function() { 79 chrome.fileBrowserPrivate.requestLocalFileSystem(function() {
70 var player = new AudioPlayer(document.querySelector('.audio-player')); 80 var player = new AudioPlayer(document.querySelector('.audio-player'));
71 function getPlaylist() { 81 function getPlaylist() {
72 chrome.mediaPlayerPrivate.getPlaylist(player.load.bind(player)); 82 chrome.mediaPlayerPrivate.getPlaylist(player.load.bind(player));
73 } 83 }
74 getPlaylist(); 84 if (document.location.hash) // The window is reloading, restore the state.
85 player.load(null);
86 else
87 getPlaylist();
75 chrome.mediaPlayerPrivate.onPlaylistChanged.addListener(getPlaylist); 88 chrome.mediaPlayerPrivate.onPlaylistChanged.addListener(getPlaylist);
76 }); 89 });
77 }; 90 };
78 91
79 /** 92 /**
80 * Load a new playlist. 93 * Load a new playlist.
81 * @param {Playlist} playlist Playlist object passed via mediaPlayerPrivate. 94 * @param {Playlist} playlist Playlist object passed via mediaPlayerPrivate.
82 */ 95 */
83 AudioPlayer.prototype.load = function(playlist) { 96 AudioPlayer.prototype.load = function(playlist) {
97 if (!playlist || !playlist.items.length) {
98 // playlist is null if the window is being reloaded.
99 // playlist is empty if ChromeOS has restarted with the Audio Player open.
100 // Restore the playlist from the local storage. Restore the player state
101 // encoded in the page location.
102 try {
103 playlist = {
104 items: JSON.parse(localStorage[AudioPlayer.PLAYLIST_KEY]),
105 position: Number(localStorage[AudioPlayer.TRACK_KEY]),
106 restore: true
107 };
108 } catch (ignore) {}
109 } else {
110 // Remember the playlist for the restart.
111 localStorage[AudioPlayer.PLAYLIST_KEY] = JSON.stringify(playlist.items);
112 localStorage[AudioPlayer.TRACK_KEY] = playlist.position;
113 }
114
84 this.playlistGeneration_++; 115 this.playlistGeneration_++;
85 116
86 this.audioControls_.pause(); 117 this.audioControls_.pause();
87 118
88 this.currentTrack_ = -1; 119 this.currentTrack_ = -1;
89 120
90 this.urls_ = playlist.items; 121 this.urls_ = playlist.items;
91 122
92 this.invalidTracks_ = {}; 123 this.invalidTracks_ = {};
93 this.cancelAutoAdvance_(); 124 this.cancelAutoAdvance_();
94 125
95 if (this.urls_.length == 1) 126 if (this.urls_.length <= 1)
96 this.container_.classList.add('single-track'); 127 this.container_.classList.add('single-track');
97 else 128 else
98 this.container_.classList.remove('single-track'); 129 this.container_.classList.remove('single-track');
99 130
100 this.syncHeight_(); 131 this.syncHeight_();
101 132
102 this.trackList_.textContent = ''; 133 this.trackList_.textContent = '';
103 this.trackStack_.textContent = ''; 134 this.trackStack_.textContent = '';
104 135
105 this.trackListItems_ = []; 136 this.trackListItems_ = [];
106 this.trackStackItems_ = []; 137 this.trackStackItems_ = [];
107 138
139 if (this.urls_.length == 0)
140 return;
141
108 for (var i = 0; i != this.urls_.length; i++) { 142 for (var i = 0; i != this.urls_.length; i++) {
109 var url = this.urls_[i]; 143 var url = this.urls_[i];
110 var onClick = this.select_.bind(this, i); 144 var onClick = this.select_.bind(this, i);
111 this.trackListItems_.push( 145 this.trackListItems_.push(
112 new AudioPlayer.TrackInfo(this.trackList_, url, onClick)); 146 new AudioPlayer.TrackInfo(this.trackList_, url, onClick));
113 this.trackStackItems_.push( 147 this.trackStackItems_.push(
114 new AudioPlayer.TrackInfo(this.trackStack_, url, onClick)); 148 new AudioPlayer.TrackInfo(this.trackStack_, url, onClick));
115 } 149 }
116 150
117 this.select_(playlist.position); 151 this.select_(playlist.position, playlist.restore);
118 152
119 // This class will be removed if at least one track has art. 153 // This class will be removed if at least one track has art.
120 this.container_.classList.add('noart'); 154 this.container_.classList.add('noart');
121 155
122 // Load the selected track metadata first, then load the rest. 156 // Load the selected track metadata first, then load the rest.
123 this.loadMetadata_(playlist.position); 157 this.loadMetadata_(playlist.position);
124 for (i = 0; i != this.urls_.length; i++) { 158 for (i = 0; i != this.urls_.length; i++) {
125 if (i != playlist.position) 159 if (i != playlist.position)
126 this.loadMetadata_(i); 160 this.loadMetadata_(i);
127 } 161 }
(...skipping 19 matching lines...) Expand all
147 AudioPlayer.prototype.displayMetadata_ = function(track, metadata, opt_error) { 181 AudioPlayer.prototype.displayMetadata_ = function(track, metadata, opt_error) {
148 this.trackListItems_[track]. 182 this.trackListItems_[track].
149 setMetadata(metadata, this.container_, opt_error); 183 setMetadata(metadata, this.container_, opt_error);
150 this.trackStackItems_[track]. 184 this.trackStackItems_[track].
151 setMetadata(metadata, this.container_, opt_error); 185 setMetadata(metadata, this.container_, opt_error);
152 }; 186 };
153 187
154 /** 188 /**
155 * Select a new track to play. 189 * Select a new track to play.
156 * @param {number} newTrack New track number. 190 * @param {number} newTrack New track number.
191 * @param {boolean} opt_restoreState True if restoring the play state from URL.
157 * @private 192 * @private
158 */ 193 */
159 AudioPlayer.prototype.select_ = function(newTrack) { 194 AudioPlayer.prototype.select_ = function(newTrack, opt_restoreState) {
160 if (this.currentTrack_ == newTrack) return; 195 if (this.currentTrack_ == newTrack) return;
161 196
162 this.changeSelectionInList_(this.currentTrack_, newTrack); 197 this.changeSelectionInList_(this.currentTrack_, newTrack);
163 this.changeSelectionInStack_(this.currentTrack_, newTrack); 198 this.changeSelectionInStack_(this.currentTrack_, newTrack);
164 199
165 this.currentTrack_ = newTrack; 200 this.currentTrack_ = newTrack;
201 localStorage[AudioPlayer.TRACK_KEY] = this.currentTrack_;
202
166 this.scrollToCurrent_(false); 203 this.scrollToCurrent_(false);
167 204
168 var url = this.urls_[this.currentTrack_]; 205 var url = this.urls_[this.currentTrack_];
169 this.fetchMetadata_(url, function(metadata) { 206 this.fetchMetadata_(url, function(metadata) {
170 var media = this.audioControls_.getMedia();
171 // Do not try no stream when offline. 207 // Do not try no stream when offline.
172 media.src = 208 var src =
173 (navigator.onLine && metadata.streaming && metadata.streaming.url) || 209 (navigator.onLine && metadata.streaming && metadata.streaming.url) ||
174 url; 210 url;
175 media.load(); 211 this.audioControls_.load(src, opt_restoreState);
176 this.audioControls_.play();
177 }.bind(this)); 212 }.bind(this));
178 }; 213 };
179 214
180 /** 215 /**
181 * @param {string} url Track file url. 216 * @param {string} url Track file url.
182 * @param {function(object)} callback Callback. 217 * @param {function(object)} callback Callback.
183 * @private 218 * @private
184 */ 219 */
185 AudioPlayer.prototype.fetchMetadata_ = function(url, callback) { 220 AudioPlayer.prototype.fetchMetadata_ = function(url, callback) {
186 this.metadataCache_.get(url, 'thumbnail|media|streaming', 221 this.metadataCache_.get(url, 'thumbnail|media|streaming',
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 // Only display the image if the thumbnail loaded successfully. 476 // Only display the image if the thumbnail loaded successfully.
442 this.art_.classList.remove('blank'); 477 this.art_.classList.remove('blank');
443 container.classList.remove('noart'); 478 container.classList.remove('noart');
444 }.bind(this); 479 }.bind(this);
445 this.img_.src = metadata.thumbnail.url; 480 this.img_.src = metadata.thumbnail.url;
446 } 481 }
447 this.title_.textContent = metadata.media.title || this.getDefaultTitle(); 482 this.title_.textContent = metadata.media.title || this.getDefaultTitle();
448 this.artist_.textContent = 483 this.artist_.textContent =
449 error || metadata.media.artist || this.getDefaultArtist(); 484 error || metadata.media.artist || this.getDefaultArtist();
450 }; 485 };
486
487 /**
488 * Audio controls specific for the Audio Player.
489 *
490 * @param {HTMLElement} container Parent container.
491 * @param {function(boolean)} advanceTrack Parameter: true=forward.
492 * @param {function} onError Error handler.
493 * @constructor
494 */
495 function FullWindowAudioControls(container, advanceTrack, onError) {
496 AudioControls.apply(this, arguments);
497 }
498
499 FullWindowAudioControls.prototype = { __proto__: AudioControls.prototype };
500
501 /**
502 * Enable play state restore from the location hash.
503 * @param {string} src Source URL.
504 * @param {boolean} restore True if need to restore the play state.
505 */
506 FullWindowAudioControls.prototype.load = function(src, restore) {
507 this.media_.src = src;
508 this.media_.load();
509 this.restoreWhenLoaded_ = restore;
510 };
511
512 /**
513 * Save the current play state in the location hash so that it survives
514 * the page reload.
515 */
516 FullWindowAudioControls.prototype.onPlayStateChanged = function() {
517 this.encodeStateIntoLocation();
518 };
519
520 /**
521 * Restore the Save the play state from the location hash.
522 */
523 FullWindowAudioControls.prototype.restorePlayState = function() {
524 if (this.restoreWhenLoaded_) {
525 this.restoreWhenLoaded_ = false; // This should only work once.
526 if (this.decodeStateFromLocation())
527 return;
528 }
529 this.play();
530 };
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/media/media_player.cc ('k') | chrome/browser/resources/file_manager/js/media/media_controls.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698