diff --git a/assets/skin-super-modern/images/ad-skip.svg b/assets/skin-super-modern/images/ad-skip.svg
new file mode 100644
index 000000000..26ee93211
--- /dev/null
+++ b/assets/skin-super-modern/images/ad-skip.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/scss/skin-super-modern/_skin-ads.scss b/src/scss/skin-super-modern/_skin-ads.scss
index 4fe18dea7..332dcb48b 100644
--- a/src/scss/skin-super-modern/_skin-ads.scss
+++ b/src/scss/skin-super-modern/_skin-ads.scss
@@ -1,40 +1,26 @@
@import 'variables';
// sass-lint:disable nesting-depth
-.#{$prefix}-ui-skin-ads {
-
- .#{$prefix}-ui-ads-status {
- background-color: $color-background-bars;
- left: 1.5em;
- padding: .5em 1.5em;
- position: absolute;
- top: 1em;
-
- .#{$prefix}-ui-label-ad-message {
- @extend %ui-label;
-
- color: $color-secondary;
- white-space: normal;
+&.#{$prefix}-ui-skin-ads {
+ @import 'components/adskipbutton';
+ @import 'components/adstatusoverlay';
+
+ .#{$prefix}-ui-seekbar {
+ .#{$prefix}-seekbar,
+ .#{$prefix}-seekbar-bars,
+ .#{$prefix}-seekbar-bars > * {
+ pointer-events: none;
}
- .#{$prefix}-ui-button-ad-skip {
- @extend %ui-button;
-
- .#{$prefix}-label {
- display: inherit;
-
- &:hover {
- text-decoration: underline;
- }
- }
+ .#{$prefix}-seekbar-playbackposition-marker,
+ .#{$prefix}-seekbar-bufferlevel,
+ .#{$prefix}-seekbar-seekposition,
+ .#{$prefix}-seekbar-markers {
+ display: none;
+ }
- // Add the dot between ad message and skip button
- &::before {
- color: $color-highlight;
- content: '●';
- padding-left: .5em;
- padding-right: .5em;
- }
+ .#{$prefix}-seekbar-playbackposition {
+ background-color: $color-ads;
}
}
diff --git a/src/scss/skin-super-modern/_variables.scss b/src/scss/skin-super-modern/_variables.scss
index 2430b3625..28cae051f 100644
--- a/src/scss/skin-super-modern/_variables.scss
+++ b/src/scss/skin-super-modern/_variables.scss
@@ -17,6 +17,7 @@ $color-item-hover: #54565a !default;
$color-background-menu: #212226 !default;
$color-background-seek-circle: rgba(124, 124, 124, .35) !default;
$color-shadow-seek-label: 0 0 30px 0 rgba(0, 0, 0, .75) !default;
+$color-ads: #ffc737 !default;
$font-family: sans-serif !default;
$font-size: 1em !default;
diff --git a/src/scss/skin-super-modern/components/_adskipbutton.scss b/src/scss/skin-super-modern/components/_adskipbutton.scss
new file mode 100644
index 000000000..c1d9f5a66
--- /dev/null
+++ b/src/scss/skin-super-modern/components/_adskipbutton.scss
@@ -0,0 +1,39 @@
+@import '../variables';
+
+%ui-button-ad-skip {
+ @extend %ui-button;
+
+ background-color: transparentize(#000, 0.75);
+ border-radius: 20px;
+ padding: 0em 1em;
+ min-width: fit-content;
+
+ .#{$prefix}-label {
+ display: inline-block;
+ color: $color-ads;
+
+ &::after {
+ background-image: url('../../assets/skin-super-modern/images/ad-skip.svg');
+ background-repeat: no-repeat;
+ background-size: 1em auto;
+ content: ' ';
+ display: inline-block;
+ height: 1em;
+ vertical-align: bottom;
+ margin-left: .5em;
+ width: 1em;
+ }
+ }
+
+ &.#{$prefix}-disabled {
+ .#{$prefix}-label {
+ &::after {
+ display: none;
+ }
+ }
+ }
+}
+
+.#{$prefix}-ui-button-ad-skip {
+ @extend %ui-button-ad-skip;
+}
diff --git a/src/scss/skin-super-modern/components/_adstatusoverlay.scss b/src/scss/skin-super-modern/components/_adstatusoverlay.scss
new file mode 100644
index 000000000..d97b6b7e1
--- /dev/null
+++ b/src/scss/skin-super-modern/components/_adstatusoverlay.scss
@@ -0,0 +1,29 @@
+@import '../variables';
+@import '../mixins';
+
+%ad-status-overlay {
+ @extend %ui-container;
+ @include layout-align-bottom;
+
+ box-sizing: border-box;
+ line-height: 1em;
+ padding: 1em 1em .5em;
+
+ .#{$prefix}-bar {
+ > .#{$prefix}-container-wrapper {
+ pointer-events: none;
+ display: flex;
+ margin: .5em 0;
+ }
+ }
+
+ // Move the overlay up above the controlbar when it appears to avoid them overlapping
+ &.#{$prefix}-controlbar-visible {
+ bottom: 5em;
+ transition: bottom $animation-duration-short ease-in;
+ }
+}
+
+.#{$prefix}-ui-ad-status-overlay {
+ @extend %ad-status-overlay;
+}
diff --git a/src/ts/components/adstatusoverlay.ts b/src/ts/components/adstatusoverlay.ts
new file mode 100644
index 000000000..85b1aa8e2
--- /dev/null
+++ b/src/ts/components/adstatusoverlay.ts
@@ -0,0 +1,47 @@
+import { Container, ContainerConfig } from './container';
+import { AdSkipButton } from './adskipbutton';
+import { Spacer } from './spacer';
+import { PlayerAPI } from 'bitmovin-player';
+import { UIInstanceManager } from '../uimanager';
+import { Component, ComponentConfig } from './component';
+import { ControlBar } from './controlbar';
+
+export class AdStatusOverlay extends Container {
+ private static readonly CLASS_CONTROLBAR_VISIBLE = 'controlbar-visible';
+
+ constructor(config: ContainerConfig = {}) {
+ super(config);
+
+ this.config = this.mergeConfig(
+ config,
+ {
+ components: [
+ new Container({
+ components: [
+ new Spacer(),
+ new AdSkipButton(),
+ ],
+ cssClasses: ['bar'],
+ }),
+ ],
+ cssClass: 'ui-ad-status-overlay',
+ },
+ this.config,
+ );
+ }
+
+ configure(player: PlayerAPI, uimanager: UIInstanceManager) {
+ super.configure(player, uimanager);
+
+ uimanager.onComponentShow.subscribe((component: Component) => {
+ if (component instanceof ControlBar) {
+ this.getDomElement().addClass(this.prefixCss(AdStatusOverlay.CLASS_CONTROLBAR_VISIBLE));
+ }
+ });
+ uimanager.onComponentHide.subscribe((component: Component) => {
+ if (component instanceof ControlBar) {
+ this.getDomElement().removeClass(this.prefixCss(AdStatusOverlay.CLASS_CONTROLBAR_VISIBLE));
+ }
+ });
+ }
+}
diff --git a/src/ts/components/seekbar.ts b/src/ts/components/seekbar.ts
index 22e086ee2..041194d62 100644
--- a/src/ts/components/seekbar.ts
+++ b/src/ts/components/seekbar.ts
@@ -569,6 +569,13 @@ export class SeekBar extends Component {
return;
}
+ // Reset the currentTimeSeekBar and set the position to 0 if the player has no duration
+ if (this.player.getDuration() === 0) {
+ this.setPlaybackPosition(0);
+ currentTimeSeekBar = 0;
+ return;
+ }
+
currentTimeSeekBar += currentTimeUpdateDeltaSecs;
try {
diff --git a/src/ts/main.ts b/src/ts/main.ts
index 82a7e80cb..e381b9536 100644
--- a/src/ts/main.ts
+++ b/src/ts/main.ts
@@ -90,6 +90,7 @@ export { SettingsPanelItem } from './components/settingspanelitem';
export { ReplayButton } from './components/replaybutton';
export { QuickSeekButton, QuickSeekButtonConfig } from './components/quickseekbutton';
export { ListSelector, ListSelectorConfig, ListItem, ListItemFilter, ListItemLabelTranslator } from './components/listselector';
+export { AdStatusOverlay } from './components/adstatusoverlay';
// Object.assign polyfill for ES5/IE9
// https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
diff --git a/src/ts/uifactory.ts b/src/ts/uifactory.ts
index f2d3f638d..e888571b5 100644
--- a/src/ts/uifactory.ts
+++ b/src/ts/uifactory.ts
@@ -56,6 +56,7 @@ import { ModernSettingsPanelItem } from './components/modernsettingspanelitem';
import { ModernSettingsPanelPage } from './components/modernsettingspanelpage';
import { ModernSettingsPanel } from './components/modernsettingspanel';
import { TouchControlOverlay } from './components/touchcontroloverlay';
+import { AdStatusOverlay } from './components/adstatusoverlay';
export namespace UIFactory {
export function buildDefaultSuperModernUI(player: PlayerAPI, config: UIConfig = {}): UIManager {
@@ -447,11 +448,93 @@ export namespace UIFactory {
}
export function superModernMobileAdsUI() {
- return new UIContainer({});
+ let controlBar = new ControlBar({
+ components: [
+ new Container({
+ components: [
+ new PlaybackTimeLabel({ timeLabelMode: PlaybackTimeLabelMode.CurrentTime }),
+ new SeekBar({ label: new SeekBarLabel() }),
+ new PlaybackTimeLabel({
+ timeLabelMode: PlaybackTimeLabelMode.TotalTime,
+ cssClasses: ['text-right'],
+ }),
+ ],
+ cssClasses: ['controlbar-top'],
+ }),
+ new Container({
+ components: [
+ new PlaybackToggleButton(),
+ new VolumeToggleButton(),
+ new Spacer(),
+ new FullscreenToggleButton(),
+ ],
+ cssClasses: ['controlbar-bottom'],
+ }),
+ ],
+ });
+
+ return new UIContainer({
+ components: [
+ new BufferingOverlay(),
+ new AdClickOverlay(),
+ new PlaybackToggleOverlay(),
+ controlBar,
+ new AdStatusOverlay(),
+ new ErrorMessageOverlay(),
+ ],
+ hideDelay: 2000,
+ hidePlayerStateExceptions: [
+ PlayerUtils.PlayerState.Prepared,
+ PlayerUtils.PlayerState.Paused,
+ PlayerUtils.PlayerState.Finished,
+ ],
+ cssClasses: ['ui-skin-super-modern', 'ui-skin-smallscreen', 'ui-skin-ads'],
+ });
}
export function superModernAdsUI() {
- return new UIContainer({});
+ let controlBar = new ControlBar({
+ components: [
+ new Container({
+ components: [
+ new PlaybackTimeLabel({ timeLabelMode: PlaybackTimeLabelMode.CurrentTime }),
+ new SeekBar({ label: new SeekBarLabel() }),
+ new PlaybackTimeLabel({
+ timeLabelMode: PlaybackTimeLabelMode.TotalTime,
+ cssClasses: ['text-right'],
+ }),
+ ],
+ cssClasses: ['controlbar-top'],
+ }),
+ new Container({
+ components: [
+ new PlaybackToggleButton(),
+ new VolumeToggleButton(),
+ new Spacer(),
+ new FullscreenToggleButton(),
+ ],
+ cssClasses: ['controlbar-bottom'],
+ }),
+ ],
+ });
+
+ return new UIContainer({
+ components: [
+ new BufferingOverlay(),
+ new AdClickOverlay(),
+ new PlaybackToggleOverlay(),
+ new AdStatusOverlay(),
+ controlBar,
+ new ErrorMessageOverlay(),
+ ],
+ hideDelay: 2000,
+ hidePlayerStateExceptions: [
+ PlayerUtils.PlayerState.Prepared,
+ PlayerUtils.PlayerState.Paused,
+ PlayerUtils.PlayerState.Finished,
+ ],
+ cssClasses: ['ui-skin-super-modern', 'ui-skin-ads'],
+ });
}
export function superModernMobileUI() {