Skip to content

Commit 3daeac6

Browse files
aveladtykus160
andauthored
feat: Add queue manager (#8649)
Close #8508 --------- Co-authored-by: Wojciech Tyczyński <[email protected]>
1 parent 0feee21 commit 3daeac6

33 files changed

+883
-60
lines changed

build/all.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ def main(args):
119119
build_args_without_ui = ['--name', 'compiled', '+@complete', '-@ui']
120120
build_args_only_dash_without_ui = [
121121
'--name', 'dash',
122-
'+@complete', '-@ui', '-@polyfillForUI',
122+
'+@complete', '-@ui', '-@polyfillForUI', '-@queue',
123123
'-@hls', '-@transmuxer', '-@mss', '-@offline', '-@cast', '-@optionalText', '-@ads',
124124
]
125125
build_args_only_hls_without_ui = [
126126
'--name', 'hls',
127-
'+@complete', '-@ui', '-@polyfillForUI',
127+
'+@complete', '-@ui', '-@polyfillForUI', '-@queue',
128128
'-@dash', '-@mss', '-@offline', '-@cast', '-@optionalText', '-@ads',
129129
]
130130

build/types/complete

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
+@manifests
99
+@polyfill
1010
+@polyfillForUI
11+
+@queue
1112
+@text
1213
+@optionalText
1314
+@transmuxer

build/types/core

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
+../../lib/config/auto_show_text.js
99
+../../lib/config/codec_switching_strategy.js
1010
+../../lib/config/cross_boundary_strategy.js
11+
+../../lib/config/repeat_mode.js
1112

1213
+../../lib/debug/asserts.js
1314
+../../lib/debug/log.js

build/types/queue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Queue Functionality
2+
3+
+../../lib/queue/queue_manager.js

build/types/ui

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
+../../ui/settings_menu.js
3838
+../../ui/small_play_button.js
3939
+../../ui/skip_ad_button.js
40+
+../../ui/skip_next_button.js
4041
+../../ui/spacer.js
4142
+../../ui/statistics_button.js
4243
+../../ui/text_selection.js

demo/asset_card.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,18 @@ shakaDemo.AssetCard = class {
259259
await shakaDemoMain.loadAsset(this.asset_);
260260
this.remakeButtons();
261261
});
262+
this.addButton('Add to queue', async () => {
263+
if (disableButtons) {
264+
return;
265+
}
266+
disableButtons = true;
267+
if (shakaDemoMain.isPlaying()) {
268+
await shakaDemoMain.addToQueue(this.asset_);
269+
} else {
270+
await shakaDemoMain.loadAsset(this.asset_);
271+
}
272+
this.remakeButtons();
273+
});
262274
let preloadName = 'Start Preload';
263275
if (this.asset_.preloadManager) {
264276
preloadName = this.asset_.preloaded ? 'Preloaded!' : 'Preloading...';

demo/common/asset.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const ShakaDemoAssetInfo = class {
4444
this.extraText = [];
4545
/** @type {!Array<string>} */
4646
this.extraThumbnail = [];
47-
/** @type {!Array<!shakaAssets.ExtraChapter>} */
47+
/** @type {!Array<!shaka.extern.ExtraChapter>} */
4848
this.extraChapter = [];
4949
/** @type {?string} */
5050
this.certificateUri = null;
@@ -403,7 +403,7 @@ const ShakaDemoAssetInfo = class {
403403
}
404404

405405
/**
406-
* @param {shakaAssets.ExtraChapter} extraChapter
406+
* @param {shaka.extern.ExtraChapter} extraChapter
407407
* @return {!ShakaDemoAssetInfo}
408408
*/
409409
addExtraChapter(extraChapter) {

demo/common/assets.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,6 @@ shakaAssets.Feature = {
182182
// Set if the asset has Common Media Server Data.
183183
CMSD: 'Common Media Server Data',
184184
};
185-
186-
187-
/**
188-
* @typedef {{
189-
* uri: string,
190-
* language: string,
191-
* mime: string
192-
* }}
193-
*
194-
* @property {string} uri
195-
* The URI of the chapter.
196-
* @property {string} language
197-
* The language of the chapter (e.g. 'en').
198-
* @property {string} mime
199-
* The MIME type of the chapter (e.g. 'text/vtt')
200-
*/
201-
shakaAssets.ExtraChapter;
202185
// End types and enums }}}
203186

204187

demo/config.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ shakaDemo.Config = class {
100100
this.addCmsdSection_();
101101
this.addLcevcSection_();
102102
this.addAdsSection_();
103+
this.addQueueManagerSection_();
103104
}
104105

105106
/**
@@ -419,6 +420,27 @@ shakaDemo.Config = class {
419420
'ads.allowStartInMiddleOfInterstitial');
420421
}
421422

423+
/** @private */
424+
addQueueManagerSection_() {
425+
const repeatModeOptions = shaka.config.RepeatMode;
426+
const repeatModeOptionNames = {
427+
'OFF': 'Off',
428+
'ALL': 'All',
429+
'SINGLE': 'Single',
430+
};
431+
432+
const docLink = this.resolveExternLink_('.QueueConfiguration');
433+
this.addSection_('Queue Manager', docLink)
434+
.addNumberInput_('Time window at end to preload next Queue item',
435+
'queue.preloadNextUrlWindow',
436+
/* canBeDecimal= */ true,
437+
/* canBeZero= */ true)
438+
.addSelectInput_('Repeat mode',
439+
'queue.repeatMode',
440+
repeatModeOptions,
441+
repeatModeOptionNames);
442+
}
443+
422444
/**
423445
* @param {string} category
424446
* @param {string} sectionName

demo/main.js

Lines changed: 85 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,22 @@ shakaDemo.Main = class {
445445
const onErrorEvent = (event) => this.onErrorEvent_(event);
446446
this.player_.addEventListener('error', onErrorEvent);
447447

448+
this.player_.addEventListener('loaded', () => {
449+
if (this.player_.isAudioOnly()) {
450+
if (this.video_.poster == shakaDemo.Main.mainPoster_) {
451+
this.video_.poster = shakaDemo.Main.audioOnlyPoster_;
452+
}
453+
} else {
454+
if (this.video_.poster == shakaDemo.Main.audioOnlyPoster_) {
455+
this.video_.poster = shakaDemo.Main.mainPoster_;
456+
}
457+
}
458+
});
459+
460+
this.player_.addEventListener('unloading', () => {
461+
this.video_.poster = shakaDemo.Main.mainPoster_;
462+
});
463+
448464
// Listen to events on controls.
449465
this.controls_ = ui.getControls();
450466
this.controls_.addEventListener('error', onErrorEvent);
@@ -1274,6 +1290,9 @@ shakaDemo.Main = class {
12741290
}
12751291
this.player_.unload();
12761292

1293+
const queueManager = this.player_.getQueueManager();
1294+
queueManager.removeAllItems();
1295+
12771296
// The currently-selected asset changed, so update asset cards.
12781297
this.dispatchEventWithName_('shaka-main-selected-asset-changed');
12791298

@@ -1286,6 +1305,14 @@ shakaDemo.Main = class {
12861305
this.remakeHash();
12871306
}
12881307

1308+
/**
1309+
* @return {boolean}
1310+
*/
1311+
isPlaying() {
1312+
const videoBar = document.getElementById('video-bar');
1313+
return !videoBar.classList.contains('hidden');
1314+
}
1315+
12891316
/**
12901317
* @param {ShakaDemoAssetInfo} asset
12911318
* @param {shaka.offline.Storage=} storage
@@ -1474,52 +1501,37 @@ shakaDemo.Main = class {
14741501
}
14751502
}
14761503

1504+
const queueManager = this.player_.getQueueManager();
1505+
queueManager.removeAllItems();
1506+
14771507
// Finally, the asset can be loaded.
14781508
if (asset.preloadManager) {
14791509
const preloadManager = asset.preloadManager;
14801510
asset.preloadManager = null;
14811511
await this.player_.load(preloadManager);
1482-
} else {
1483-
const manifestUri = await this.getManifestUri_(asset);
1484-
let mimeType = undefined;
1485-
if (asset.mimeType &&
1486-
manifestUri && !manifestUri.startsWith('offline:')) {
1487-
mimeType = asset.mimeType;
1488-
}
1489-
await this.player_.load(
1490-
manifestUri,
1491-
/* startTime= */ null,
1492-
mimeType);
1493-
}
1494-
1495-
if (this.player_.isAudioOnly() &&
1496-
this.video_.poster == shakaDemo.Main.mainPoster_) {
1497-
this.video_.poster = shakaDemo.Main.audioOnlyPoster_;
1498-
}
14991512

1500-
if (!(asset.storedContent && asset.storedContent.offlineUri)) {
1501-
for (const extraText of asset.extraText) {
1502-
if (extraText.mime) {
1503-
this.player_.addTextTrackAsync(extraText.uri, extraText.language,
1504-
extraText.kind, extraText.mime, extraText.codecs);
1505-
} else {
1506-
this.player_.addTextTrackAsync(extraText.uri, extraText.language,
1507-
extraText.kind);
1513+
if (!(asset.storedContent && asset.storedContent.offlineUri)) {
1514+
for (const extraText of asset.extraText) {
1515+
if (extraText.mime) {
1516+
this.player_.addTextTrackAsync(extraText.uri, extraText.language,
1517+
extraText.kind, extraText.mime, extraText.codecs);
1518+
} else {
1519+
this.player_.addTextTrackAsync(extraText.uri, extraText.language,
1520+
extraText.kind);
1521+
}
1522+
}
1523+
for (const extraThumbnail of asset.extraThumbnail) {
1524+
this.player_.addThumbnailsTrack(extraThumbnail);
15081525
}
15091526
}
1510-
for (const extraThumbnail of asset.extraThumbnail) {
1511-
this.player_.addThumbnailsTrack(extraThumbnail);
1512-
}
1513-
}
1514-
1515-
for (const extraChapter of asset.extraChapter) {
1516-
if (extraChapter.mime) {
1527+
for (const extraChapter of asset.extraChapter) {
15171528
this.player_.addChaptersTrack(
15181529
extraChapter.uri, extraChapter.language, extraChapter.mime);
1519-
} else {
1520-
this.player_.addChaptersTrack(
1521-
extraChapter.uri, extraChapter.language);
15221530
}
1531+
} else {
1532+
const queueItem = await this.getQueueItem_(asset);
1533+
queueManager.insertItems([queueItem]);
1534+
await queueManager.playItem(0);
15231535
}
15241536

15251537
// Set media session title, but only if the browser supports that API.
@@ -1550,6 +1562,44 @@ shakaDemo.Main = class {
15501562
this.remakeHash();
15511563
}
15521564

1565+
/**
1566+
* @param {ShakaDemoAssetInfo} asset
1567+
*/
1568+
async addToQueue(asset) {
1569+
const queueManager = this.player_.getQueueManager();
1570+
const queueItem = await this.getQueueItem_(asset);
1571+
queueManager.insertItems([queueItem]);
1572+
}
1573+
1574+
/**
1575+
* @param {ShakaDemoAssetInfo} asset
1576+
* @return {!Promise<shaka.extern.QueueItem>}
1577+
* @private
1578+
*/
1579+
async getQueueItem_(asset) {
1580+
const manifestUri = await this.getManifestUri_(asset);
1581+
let mimeType = null;
1582+
if (asset.mimeType &&
1583+
manifestUri && !manifestUri.startsWith('offline:')) {
1584+
mimeType = asset.mimeType;
1585+
}
1586+
const itemConfig = asset.getConfiguration();
1587+
shaka.util.PlayerConfiguration.mergeConfigObjects(
1588+
itemConfig, this.desiredConfig_, this.defaultConfig_);
1589+
const isOffline = asset.storedContent && asset.storedContent.offlineUri;
1590+
/** @type {shaka.extern.QueueItem} */
1591+
const queueItem = {
1592+
manifestUri: manifestUri,
1593+
startTime: null,
1594+
mimeType: mimeType,
1595+
config: itemConfig,
1596+
extraText: isOffline ? null : asset.extraText,
1597+
extraThumbnail: isOffline ? null : asset.extraThumbnail,
1598+
extraChapter: asset.extraChapter,
1599+
};
1600+
return queueItem;
1601+
}
1602+
15531603
/** Remakes the location's hash. */
15541604
remakeHash() {
15551605
if (!this.fullyLoaded_) {

externs/shaka/player.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,6 +2717,7 @@ shaka.extern.TextDisplayerConfiguration;
27172717
* preferredDecodingAttributes: !Array<string>,
27182718
* preferForcedSubs: boolean,
27192719
* preferSpatialAudio: boolean,
2720+
* queue: shaka.extern.QueueConfiguration,
27202721
* restrictions: shaka.extern.Restrictions,
27212722
* playRangeStart: number,
27222723
* playRangeEnd: number,
@@ -2843,6 +2844,8 @@ shaka.extern.TextDisplayerConfiguration;
28432844
* If true, a spatial audio track is preferred.
28442845
* <br>
28452846
* Defaults to <code>false</code>.
2847+
* @property {shaka.extern.QueueConfiguration} queue
2848+
* Queue manager configuration and settings.
28462849
* @property {shaka.extern.Restrictions} restrictions
28472850
* The application restrictions to apply to the tracks. These are "hard"
28482851
* restrictions. Any track that fails to meet these restrictions will not
@@ -2868,6 +2871,30 @@ shaka.extern.TextDisplayerConfiguration;
28682871
shaka.extern.PlayerConfiguration;
28692872

28702873

2874+
/**
2875+
* @typedef {{
2876+
* preloadNextUrlWindow: number,
2877+
* repeatMode: shaka.config.RepeatMode
2878+
* }}
2879+
*
2880+
* @description
2881+
* The Queue Manager's configuration options.
2882+
*
2883+
* @property {number} preloadNextUrlWindow
2884+
* The window of time at the end of the presentation to begin preloading the
2885+
* next item. Measured in seconds. If the value is 0, the next URL will not
2886+
* be preloaded at all.
2887+
* <br>
2888+
* Defaults to <code>30</code>.
2889+
* @property {shaka.config.RepeatMode} repeatMode
2890+
* Controls behavior of the queue when all items have been played.
2891+
* <br>
2892+
* Defaults to {@link shaka.config.RepeatMode#OFF}.
2893+
* @exportDoc
2894+
*/
2895+
shaka.extern.QueueConfiguration;
2896+
2897+
28712898
/**
28722899
* @typedef {{
28732900
* language: string,
@@ -2989,3 +3016,20 @@ shaka.extern.Chapter;
29893016
* @exportDoc
29903017
*/
29913018
shaka.extern.ExtraText;
3019+
3020+
3021+
/**
3022+
* @typedef {{
3023+
* uri: string,
3024+
* language: string,
3025+
* mime: string
3026+
* }}
3027+
*
3028+
* @property {string} uri
3029+
* The URI of the chapter.
3030+
* @property {string} language
3031+
* The language of the chapter (e.g. 'en').
3032+
* @property {string} mime
3033+
* The MIME type of the chapter (e.g. 'text/vtt')
3034+
*/
3035+
shaka.extern.ExtraChapter;

0 commit comments

Comments
 (0)