Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new advertising UI layout #660

Merged
merged 10 commits into from
Dec 20, 2024
3 changes: 3 additions & 0 deletions assets/skin-super-modern/images/ad-skip.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 17 additions & 31 deletions src/scss/skin-super-modern/_skin-ads.scss
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styling for the ad-skip-button was extracted into a separate file.

@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;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/scss/skin-super-modern/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
$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;

Check warning on line 20 in src/scss/skin-super-modern/_variables.scss

View workflow job for this annotation

GitHub Actions / test_and_build

Hex notation should all be lower case

$font-family: sans-serif !default;
$font-size: 1em !default;
Expand Down
39 changes: 39 additions & 0 deletions src/scss/skin-super-modern/components/_adskipbutton.scss
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH i find the yellow text hard to read, are we sure we want to stick with that yellow?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. I'll take that feedback back to the design process.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't your eyes hurt when looking at the above screenshot and trying to figure out how may seconds there are left until skip?


&::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;
}
29 changes: 29 additions & 0 deletions src/scss/skin-super-modern/components/_adstatusoverlay.scss
Original file line number Diff line number Diff line change
@@ -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;
}
47 changes: 47 additions & 0 deletions src/ts/components/adstatusoverlay.ts
Original file line number Diff line number Diff line change
@@ -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<ContainerConfig> {
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<ComponentConfig>) => {
if (component instanceof ControlBar) {
this.getDomElement().addClass(this.prefixCss(AdStatusOverlay.CLASS_CONTROLBAR_VISIBLE));
}
});
uimanager.onComponentHide.subscribe((component: Component<ComponentConfig>) => {
if (component instanceof ControlBar) {
this.getDomElement().removeClass(this.prefixCss(AdStatusOverlay.CLASS_CONTROLBAR_VISIBLE));
}
});
}
}
7 changes: 7 additions & 0 deletions src/ts/components/seekbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,13 @@ export class SeekBar extends Component<SeekBarConfig> {
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;
}
Comment on lines +572 to +577
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was no problem before as there was no seekbar visible during ad playback.


currentTimeSeekBar += currentTimeUpdateDeltaSecs;

try {
Expand Down
1 change: 1 addition & 0 deletions src/ts/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
87 changes: 85 additions & 2 deletions src/ts/uifactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down
Loading