-
Notifications
You must be signed in to change notification settings - Fork 15
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
Netflix plugin #23
Netflix plugin #23
Conversation
Context related code in this commit is disabled due to this weird context-related behavior in Lipsurf #24 |
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.
Good job! Some questions and requests in the comments. Also, the plugin in dist/ should be built with build:prod
src/Netflix/Netflix.ts
Outdated
return handleChangeAudioAnswer(command); | ||
} | ||
} | ||
} catch { |
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.
Better to not catch in that case if anything to let the error propagate.
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 notice you catch errors in many places below. Sometimes it makes more sense to let the error propagate up. Right now, the page will catch pageFn errors and log them so we know which commands are failing where. But also we will likely give some sort of user feedback when a command fails (eg. make the live transcript turn red) in a subsequent version.
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 actually intend to catch only the JSON.parse
part. Not the other.
I think I'll make that clearer. The usage will be something like this.
consumeCommand(maybeCommand, proofKey, (command) => {
...
})
src/Netflix/Netflix.ts
Outdated
|
||
// Context | ||
|
||
/** |
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.
comment can be removed now
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 this
src/Netflix/Netflix.ts
Outdated
contexts: { | ||
[NetflixPluginContextEnum.watch]: { | ||
commands: [ | ||
"Watch::Pause", |
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.
These don't match the format of existing commands. You should take a look at the other plugins so things are consistent.
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.
Sure. I'll rename them
src/Netflix/Netflix.ts
Outdated
const tracks = sub.texts; | ||
const [index] = await PluginBase.util.fuzzyHighScore( | ||
sub.query, | ||
tracks.map(track => track.displayName).map(stripNonAlphaNumericAndTrim), |
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.
hmm, fuzzyHighScore should do both invalid char stripping and trimming internally. Did you have problems without this stripNonAlphaNumericAndTrim? Can you give me examples?
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 actually did this on a whim. I did not run it without that stripNonAlphaNumericAndTrim
. Should I remove it?
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. Also, the version of this that platform uses is language-aware so it's "smarter"
src/Netflix/Netflix.ts
Outdated
// Messaging | ||
|
||
const LIPSURF_BOOT_SCRIPT_ID = "lipsurf-netflix-script"; | ||
const TO_PAGE_PROOF_KEY = String(Math.floor(Math.random() * 1000)); |
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 is cool! Any reason to not just use a static string prepended with PluginBase.util.getNoCollisionUniqueAttr()
?
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.
1.) I didn't realize it is there when I skimmed the docs and 2.) I didn't expect that function to exists in the platform utils (probably the latter influence the former).
I'll change it to that
src/Netflix/Netflix.ts
Outdated
|
||
{ | ||
name: "Watch::Title", | ||
match: ["watch *"], |
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 is fine, but ideally we would only match valid shows. So you could cache the shows and then use a matchFn with the util's fuzzy matching algorithm to pick the right one. It might be performance prohibitive depending on how big the show db is.
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 am optimistic about devices that can run Chrome, can withhold fuzzy matching for tens of thousands less-than-100-bytes utf8 string. And shows stored on Netflix's cache is probably can reach just about hundreds.
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 also found out that LipSurf reads Aria-Label so I kind of utilize that rather than the fuzzy matching, but I guess I can pull out the data from Netflix cache back to the extension context ala "ChangeText" and "ChangeAudio".
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 ended up combining fuzzyHighScore with 0.8 minimum confidence and falling back to search if no matching videos. The overall UX feels better that way. I found a weird behavior with fuzzyHighScore though.
When I manually map an array of videos to fuzzyHighScore with the video as the only source and then sort the result in descending order, I got a different result to when I call the fuzzyHighScore with all videos put to sources.
const [index,title] = await fuzzyHighScores(query, videoTitles, 0, true);
returns a different result (highest score) than the code below
cosnt {index, title} = (await Promise.all(
videoTitles.map(async title => ({
score: await fuzzyHighScores(query, [title], 0, true)
}))
))
// Maps array index as the index, rather than the fuzzyIndex which is always 0
.map(([_indexFromFuzzy, score], arrayIndex) => ({
score,
index: arrayIndex
}))
// Sort descending
.sort(({scoreA},{scoreB}) => scoreB - scoreA)[0]
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 ended up combining fuzzyHighScore with 0.8 minimum confidence and falling back to search if no matching videos. The overall UX feels better that way." that sounds good.
"I got a different result" -- that doesn't seem right. Can you give me the inputs you got those results with?
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.
Here you go.
There's also the code and result.
https://gist.github.com/Kelerchian/df50a53a9ef08cece06fdc326a9c1cfe
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.
First PR always gets a very thorough review!
} | ||
}; | ||
|
||
const consumeMessageStringAsCommand = ( |
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 the JSON parsing fails, then message is null and the consumeMessageStringAsCommand
cb never gets called. That wouldn't be straightforward to track down. On the other hand, if you don't catch the JSON.parse
error, it propogates up, gets caught by bugsnag and/or the developer looking at the console will see it right away. Hence I think it would be better to just call JSON.parse without any try-catch.
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 have a counter-argument for this.
In this plugin's context, a valid inter-context command message is defined as:
- a JSON
- has a
proofKey
field with a certain value determined at the start of the javascript runtime - has a
payload
field with type Command
consumeMessageStringAsCommand
is used to consume string as a command message and is used in the "message" event on windows. There's a chance Netflix have inter-frame message-passing mechanisms somewhere in their app and those message, while probably are also JSON, can be in other format too because JSON is not in the spec.
So my argument is if a string coming from window.addEventListener("message", ...)
is not a valid Message then the plugin should not be concerned with it. Because the plugin should not be concerned with it, any error that comes from it should not be handled, because it is irrelevant. Therefore, in this case, it is better for consumeMessageStringAsCommand
to silently swallow any errors that come from invalid incoming data.
If it's the debug-friendliness you are concerned with, this scenario fits more into unit-tests rather than runtime-debugging, staying close to "type error should be resolved on compile-time" principle.
That's my two cents, but the final decision will stay in your hands.
What do you think?
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.
Actually, I understand what you mean better now. This is good and fine. You can leave it as-is.
src/Netflix/Netflix.ts
Outdated
@@ -333,7 +373,7 @@ export default <IPluginBase & IPlugin>{ | |||
normal: false | |||
}, | |||
{ | |||
name: "ChangeText", | |||
name: "Change Text", |
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.
Didn't catch this last time, but "Change Text" could be clearer. Perhaps "Change Subtitle Language" Also, the match phrase could have an "[/language]" after "[text/subtitle]"
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.
Changing "Change Text" to "Change Subtitle Language". FYI Text
is Netflix's formal definition of subtitle.
Skipping the "[/language]". The command "Change Audio" also deals with language. That makes the word "language" ambiguous to the user.
src/Netflix/Netflix.ts
Outdated
@@ -333,7 +373,7 @@ export default <IPluginBase & IPlugin>{ | |||
normal: false | |||
}, | |||
{ | |||
name: "ChangeText", | |||
name: "Change Text", | |||
match: ["[change/switch] [text/subtitle] to *", "[text/subtitle] to *"], |
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 "[/change /switch ][text/subtitle] to *"
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.
Nice
src/Netflix/Netflix.ts
Outdated
|
||
{ | ||
name: "Watch::Title", | ||
match: ["watch *"], |
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 ended up combining fuzzyHighScore with 0.8 minimum confidence and falling back to search if no matching videos. The overall UX feels better that way." that sounds good.
"I got a different result" -- that doesn't seem right. Can you give me the inputs you got those results with?
src/Netflix/Netflix.ts
Outdated
const results = ( | ||
await Promise.all( | ||
filteredVideos.map(video => | ||
PluginBase.util.fuzzyHighScore(strippedQuery, [video.title], 0, true) |
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.
you shouldn't need to strip the query, that string gets preprocessed the same way the sources do.
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.
Ok
src/Netflix/Netflix.ts
Outdated
|
||
// Filter out videos which title are empty string | ||
const filteredVideos = videos.filter( | ||
video => !!stripNonAlphaNumericAndTrim(video.title) |
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.
shouldn't need to do the stripping 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.
Ok. Keeping the trim because videos sometimes has empty string as title
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 need trim either.
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.
Same as below
Notice that this is not .map. This does not change the title by trimming, this removes video data which title is empty.
This relates more to data cleanup rather than data processing itself (the fuzzyHighScore).
I initially wrote this because of the fuzzyHighScore returns different result when mapped vs unmapped.
I think this is unecessary with it written mapped (result sorted manually)
src/Netflix/Netflix.ts
Outdated
const tracks = sub.texts; | ||
const [index] = await PluginBase.util.fuzzyHighScore( | ||
sub.query, | ||
tracks.map(track => track.displayName).map(stripNonAlphaNumericAndTrim), |
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. Also, the version of this that platform uses is language-aware so it's "smarter"
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 couldn't reproduce the context dupes issue. I put this in init
. Can you give me a minimal sample to reproduce the issue?
// prints: before Normal
console.log('before ' + PluginBase.util.getContext());
PluginBase.util.addContext('new context');
PluginBase.util.addContext('new context');
// prints: after Normal, new context
console.log('after ' + PluginBase.util.getContext());
@@ -360,7 +357,7 @@ export default <IPluginBase & IPlugin>{ | |||
}, | |||
{ | |||
name: "Change Audio", | |||
match: ["[change/switch] audio to *", "audio to *"], | |||
match: ["[/change/switch] audio to *"], |
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 will create a match string with a leading space. You should include the space like this: "[/change /switch ]audio to *"
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.
(other match strings below also need to be adjusted for this leading space)
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 see.
Saying "audio to *" works though with this. Will change to that.
Sorry, haven't replied to some of your previous comments yet but closed the review. Will reply to them soon. |
Take it easy.
I'll give you this later. |
Sorry, it turns out I made a noob mistake. This is a false alarm. |
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 is basically ready. Just some minor things left. Thanks for you patience.
src/Netflix/Netflix.ts
Outdated
switch (true) { | ||
case context === NetflixPluginContextEnum.browse: { | ||
PluginBase.util.removeContext(NetflixPluginContextEnum.watch); | ||
PluginBase.util.addContext(NetflixPluginContextEnum.browse); |
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.
Can you try using enterContext to be more explicit about the context ordering? Also I think it will fix the "search" homophone issue.
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 will need a quite complex sequence to ensure tag
works, though. I'll try using enterContext
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 works. I'm not sure it is ideal though.
* TODOs: | ||
* - Remove ContextPatcher stuff when context duplication caused by PluginBase.util.addContext is fixed | ||
* - Remove homophone `"search": "search"` after homophone priority issue is fixed on the Platform-land | ||
* https://discuss.lipsurf.com/t/homophones-must-be-a-lower-priority-than-other-plugins-command/297/3 |
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 the issue is that the Normal context is taking priority because it's appearing first in the context order. If you explicitly enter the context with Normal coming after the Netflix-specific contexts, I think this will be fixed. Sorry, docs didn't mention this this so I've updated them: https://docs.lipsurf.com/contexts.html#context-order
} | ||
}; | ||
|
||
const consumeMessageStringAsCommand = ( |
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.
Actually, I understand what you mean better now. This is good and fine. You can leave it as-is.
Merged into monorepo. Nice work! |
No description provided.