-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Improve flexibility of quality selector #1081
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
base: develop
Are you sure you want to change the base?
Conversation
@friday @philipgiuliani: Kindly take a look at this and provide feedback |
PR should really be against develop |
Ah, my bad. Updated. |
src/js/plyr.js
Outdated
const value = closest(options, quality); | ||
this.debug.warn(`Unsupported quality option: ${quality}, using ${value} instead`); | ||
quality = value; | ||
if (input instanceof String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use the type checkers we have? e.g. is.string(input)
src/js/plyr.js
Outdated
quality = { | ||
value: input, | ||
}; | ||
} else if (input instanceof Object) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use the type checkers we have? e.g. is.object(input)
src/js/plugins/youtube.js
Outdated
set(input) { | ||
instance.setPlaybackQuality(mapQualityUnit(input)); | ||
let label; | ||
if (input instanceof String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use the type checkers we have? e.g. is.string(input)
src/js/plugins/youtube.js
Outdated
let label; | ||
if (input instanceof String) { | ||
label = input; | ||
} else if (input instanceof Object) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use the type checkers we have? e.g. is.object(input)
src/js/plugins/youtube.js
Outdated
|
||
return dedupe(levels.map(level => mapQualityUnit(level))); | ||
const mappedLevels = dedupe(levels.map(level => mapQualityUnit(level))); | ||
const result = mappedLevels.map((level, index) => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can just be an early return since you're returning it on the next line anyway.
Thanks for your work on this 👍
I think having the fallback would be needed to allow the minimum effort. Not everyone will want to pass their own badge text. We can use the |
I don't know how to be a 100% whether this is/isn't a breaking change. Currently, on plyr.io, Also, at the moment, I just noticed that there seems to be a bug with the setter |
Thanks for this! I'll take a look at it soon. Can also check if it's breaking then. Can you fix the linting errors apart from the |
7ca36bb
to
411d5b2
Compare
src/js/plugins/youtube.js
Outdated
if (is.string(input)) { | ||
label = input; | ||
} else if (is.object(input)) { | ||
label = input.label; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know how to fix this linting error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Destructuring is when you do this:
const {label} = input;
Instead of this:
const label = input.label;
Since it's already declared here you need to wrap with parenthesis:
({label} = input);
411d5b2
to
faaeb5d
Compare
src/js/controls.js
Outdated
// Set options if passed | ||
if (is.array(options)) { | ||
this.options.quality = dedupe(options).filter(quality => this.config.quality.options.includes(quality)); | ||
this.options.quality = dedupe(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dedupe
doesn't work on objects. It doesn't do "deep equals".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall I replace this with just
if (is.array(options) {
this.options.quality = options
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sam might disagree but at least it won't look like it's doing something and then it doesn't work)
src/js/plugins/youtube.js
Outdated
label: levels[index], | ||
index, | ||
height: level, | ||
badge: level >= 720 ? 'HD' : 'SD', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you set the badge here the i18n won't work, right? With the fallback you don't need to anyway right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing badge in favor of default logic
height: size, | ||
index, | ||
})); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are multiple problems here.
- Not really a problem per se, but as Sam pointed out there's no need for the intermediate variable
sizes
. You can just add to the existing function chain. - The label will be
720
not720p
as I'm sure you intended it to be (since otherwise the template literals doesn't really make sense). - However I don't think you should set the label at all since later on in the code you need to be able to distinguish between data that was specified by developers and fallback data in order to respect the data specified by developers. I would use a class for the entries in
this.options.qualities
in order to solve this via getters and setters (moving the logic fromgetLabel
andgetBadge
) - It wouldn't be possible to set the
badge
andlabel
via the source element. filter(Boolean)
removes allfalsy
values. I think with the other changes in this PR this isn't something we want. Especially not before grabbing the array index, like now. Rather, we should do that when creating the menu.
I suggest you change it to something like this:
// I haven't tested this code, and the comments are meant for you, not to stay in the code
return html5.getSources.call(this)
.map((source, index) => {
// Get data from the dataset (https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset)
const {
// For backward-compatibility, fallback on the old size-attribute (which I think we should deprecate)
height = source.getAttribute('size'),
label,
badge,
} = source.dataset;
return {
index, // Really don't think we need index here at all, as mentioned
badge,
label,
height,
};
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The template literal is to ensure that label conforms to the laid down rule (by me) that label is a String
.
Updated with your suggestion. This is neat. I like being able to specify label, badge via data-
attributes.
* @param {number} input - Quality level | ||
*/ | ||
set quality(input) { | ||
const config = this.config.quality; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old quality setter code was already too complex and hard to understand, so I think this is a step in the wrong direction. I think you should restore the old setter and the getter in the html5/youtube plugins and add a new getter/setter that sets only by index. videoTrack
or level
perhaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Part of your comment, I think, is referring to the fact that this setter is trying to account for number/string/object all-in-one.
The number version was added just now to be backwards compatible. At least with plyr.io. It wasn't meant to handle index.
The string version is to cater to users wanting to set it by label.
The object version is mainly to handle the internal implementation wherein:
user clicks quality option --> translate event.target.value
into quality somehow. This was where the object came into play.
Now, with the suggestion to remove index attribute and use it as the value while creating radio button, we can update the quality setter to receive, for example, either ["0", 0, { index: 0 }]
. I opted for the object implementation to allow sufficient differentiation.
src/js/controls.js
Outdated
// TODO: Try to find a badge based on height | ||
return null; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- This can be done a lot simpler with destructuring and default parameters.
- Shouldn't it be
quality.height
. Notquality.label
?
Suggestion (not tested):
const getBadge = quality => {
const {
height,
badge = i18n.get(`qualityBadge.${height}`, this.config);
} = quality;
return badge ? controls.createBadge.call(this, label) : null;
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although badge should be based on height in theory, piggy-backing on the existing i18n
requires the height to be 480
. A value of 482
(if it were the case) would generate no badge despite being the real height of the video. I think I will update this to implement the closest solution Sam was mentioning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what comment by Sam you're referring to, but you don't have to fix that. In fact, since it's a separate problem it's better not to imo. You can do that in a separate pr in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's referenced here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. I did read that, but too quickly I guess. I still think this should be a separate PR optimally. Solving it with closest also means 719p would count as HD, which it by definition isn't. I would expect it work like breakpoints, if anything. Sorry I'm making this hard for you btw!
src/js/controls.js
Outdated
type, | ||
title: controls.getLabel.call(this, 'quality', quality), | ||
title: controls.getLabel.call(this, 'quality', quality.height, toTitleCase(quality.label)), | ||
badge: getBadge(quality), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should respect the label and its formatting as it was specified by the developers, and only call getLabel if they didn't pass a label.
This includes i18n and changing the casing. In most languages I know of title case is incorrect formatting, and it make no sense to give developers the option to translate their own input. They should specify it in the language they want it.
// Translate a value into a nice label | ||
getLabel(setting, value) { | ||
getLabel(setting, value, defaultLabel) { | ||
let label; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should restore getLabel (see above). Not sure why you removed ${value}p
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is 272p
or 408p
legitimate? My understanding from your post on Slack was that the 'p' represents widescreen resolutions. Formally it's progressive scanning rather than interlaced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No that was K
As in 2K
or 4K
video etc (used for badges). 408p
basically means its height is 408px (although the "p" does mean something else entirely).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, replacing with the old logic for now.
return source && Number(source.getAttribute('size')); | ||
// Return label, if match is found | ||
return qualityLevels[sourceIndex].label; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be height
, not label
// Update UI | ||
controls.updateSetting.call(this.player, 'quality', null, event.detail.quality); | ||
controls.updateSetting.call(this.player, 'quality', null, event.detail.quality.label); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be height
, not label
// Get first match for requested size | ||
const source = sources.find(source => Number(source.getAttribute('size')) === input); | ||
const source = sources.find(source => source.getAttribute('size') === input.label); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be height
, not label
No worries at all. I'd rather get it right than have others have to fix
issues I introduce later.
…On Sun, Jul 1, 2018 at 6:02 AM, friday ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/js/controls.js
<#1081 (comment)>:
>
- if (!label.length) {
- return null;
+ if (!label.length) {
+ // TODO: Try to find a badge based on height
+ return null;
+ }
}
Alright. I did read that, but too quickly I guess. I still think this
should be a separate PR optimally. Solving it with closest also means 719p
would count as HD, which it by definition isn't. I would expect it work
like breakpoints, if anything. Sorry I'm making this hard for you btw!
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1081 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ACzB15oor-ZOB7wGv01wkseVVntYDtFBks5uCJ47gaJpZM4U-JyS>
.
|
Good point. It should be the second option then I guess. If larger or equal to 720 then it’s HD, larger than or equal to 1440 then it’s QHD etc. My point was I don’t think we need to force users to specify the labels. If they do it should override but if they don’t then we use sensible defaults to make life easy as it is now.
Sam
… On 1 Jul 2018, at 8:02 pm, friday ***@***.***> wrote:
@friday commented on this pull request.
In src/js/controls.js:
>
- if (!label.length) {
- return null;
+ if (!label.length) {
+ // TODO: Try to find a badge based on height
+ return null;
+ }
}
Alright. I did read that, but too quickly I guess. I still think this should be a separate PR optimally. Solving it with closest also means 719p would count as HD, which it by definition isn't. I would expect it work like breakpoints, if anything. Sorry I'm making this hard for you btw!
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Instead, index is taken as the array index and passed around internally where required
If unavailable, it is replaced with ${height}p
I've incorporated most of the code-related feedback and made some fixes. Inviting more comments/feedback. If required, I can add the default badge logic in this PR as well. I'm currently hesitant to make a separate PR for it as it would lead to some annoying merge conflicts if this PR were to be approved. |
I would like some feedback on this PR. If you use Plyr and you care about the quality setting, please have a read and provide feedback.
Link to related issue (if applicable)
This is related to #878 and also PR #988
Summary of proposed changes
The 'index' field is optional and is used to resolve conflicts. An example of where this may come in handy is provided below.
Finally, the badge field allows the provider to explicitly specify a badge for a particular quality level. This is somewhat a double-edged sword. On one hand, this gives full freedom to providers to determine the badges for each quality level. On the other, it may lead to inconsistencies where one provider reports
HD
for a video with height of 720 whereas another provider does not.Imagine a scenario where the available quality levels are as follows:
In the above example, with the current implementation, the only attribute that is passed on to the provider on
qualityrequested
isvalue
. If the provider implemented this similar to how YouTube is currently implemented (purely based on height), then, there would be two quality entries with the samevalue
. My hope is that the index field will help with resolution in such cases by telling the provider exactly which quality element was selected.Even after #988 has been merged in, the quality badge relies heavily on some hard-coded values. If a video presents a quality value that is not found in [480, 576, 720, 1080, 1440, 2160], then no badge is provided. In the above example, none of the entries would get a badge (if the
value
was determined by height).Instead, this PR allows providers to explicitly specify a badge of their choice. We could also add some fallback logic where the 'height' attribute is used to determine the badge.
Here's a pen that shows the results of this PR for HTML5/HLS/YouTube.
Checklist
develop
as the base branchi18n
undefined
)plyr.quality = '...'
works