mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2024-11-10 17:17:45 +01:00
commit
2c52b9e600
28 changed files with 21129 additions and 433 deletions
|
@ -2,5 +2,5 @@
|
|||
"serverAddress": "https://sponsor.ajay.app",
|
||||
"testingServerAddress": "https://sponsor.ajay.app/test",
|
||||
"serverAddressComment": "This specifies the default SponsorBlock server to connect to",
|
||||
"categoryList": ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic"]
|
||||
"categoryList": ["sponsor", "selfpromo", "interaction", "poi_highlight", "intro", "outro", "preview", "music_offtopic"]
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"version": "2.2",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_Description__",
|
||||
"homepage_url": "https://sponsor.ajay.app",
|
||||
"content_scripts": [{
|
||||
"run_at": "document_start",
|
||||
"matches": [
|
||||
|
@ -40,8 +41,11 @@
|
|||
"icons/help.svg",
|
||||
"icons/report.png",
|
||||
"icons/close.png",
|
||||
"icons/skipIcon.svg",
|
||||
"icons/refresh.svg",
|
||||
"icons/beep.ogg",
|
||||
"icons/pause.svg",
|
||||
"icons/stop.svg",
|
||||
"icons/PlayerInfoIconSponsorBlocker.svg",
|
||||
"icons/PlayerDeleteIconSponsorBlocker.svg",
|
||||
"popup.html",
|
||||
|
|
20154
package-lock.json
generated
20154
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -11,8 +11,8 @@
|
|||
"babel-loader": "^8.0.6",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0"
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chrome": "0.0.91",
|
||||
|
|
|
@ -284,8 +284,17 @@
|
|||
"skip_category": {
|
||||
"message": "Skip {0}?"
|
||||
},
|
||||
"skip_to_category": {
|
||||
"message": "Skip to {0}?",
|
||||
"description": "Used for skipping to things (Skip to Highlight)"
|
||||
},
|
||||
"skipped": {
|
||||
"message": "Skipped"
|
||||
"message": "{0} Skipped",
|
||||
"description": "Example: Sponsor Skipped"
|
||||
},
|
||||
"skipped_to_category": {
|
||||
"message": "Skipped to {0}",
|
||||
"description": "Used for skipping to things (Skipped to Highlight)"
|
||||
},
|
||||
"disableAutoSkip": {
|
||||
"message": "Disable Auto Skip"
|
||||
|
@ -347,9 +356,6 @@
|
|||
"createdBy": {
|
||||
"message": "Created By"
|
||||
},
|
||||
"autoSkip": {
|
||||
"message": "Auto Skip"
|
||||
},
|
||||
"showSkipNotice": {
|
||||
"message": "Show Notice After A Segment Is Skipped"
|
||||
},
|
||||
|
@ -544,14 +550,20 @@
|
|||
"category_music_offtopic_short": {
|
||||
"message": "Non-Music"
|
||||
},
|
||||
"category_poi_highlight": {
|
||||
"message": "Highlight"
|
||||
},
|
||||
"category_poi_highlight_description": {
|
||||
"message": "The part of the video that most people are looking for. Similar to \"Video starts at x\" comments."
|
||||
},
|
||||
"category_livestream_messages": {
|
||||
"message": "Livestream: Donation/Message Readings"
|
||||
},
|
||||
"category_livestream_messages_short": {
|
||||
"message": "Message Reading"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Disable"
|
||||
"autoSkip": {
|
||||
"message": "Auto Skip"
|
||||
},
|
||||
"manualSkip": {
|
||||
"message": "Manual Skip"
|
||||
|
@ -559,6 +571,18 @@
|
|||
"showOverlay": {
|
||||
"message": "Show In Seek Bar"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Disable"
|
||||
},
|
||||
"autoSkip_POI": {
|
||||
"message": "Auto skip to the start"
|
||||
},
|
||||
"manualSkip_POI": {
|
||||
"message": "Ask when video loads"
|
||||
},
|
||||
"showOverlay_POI": {
|
||||
"message": "Show In Seek Bar"
|
||||
},
|
||||
"autoSkipOnMusicVideos": {
|
||||
"message": "Auto skip all segments when there is a non-music segment"
|
||||
},
|
||||
|
|
|
@ -106,12 +106,10 @@
|
|||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
.sponsorBlockSpacer {
|
||||
|
@ -131,17 +129,18 @@
|
|||
right: 10px;
|
||||
}
|
||||
|
||||
.sponsorSkipNotice {
|
||||
min-width: 350px;
|
||||
max-width: 50%;
|
||||
background-color: rgba(28, 28, 28, 0.9);
|
||||
.sponsorSkipNoticeParent {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
|
||||
bottom: 100px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeParent, .sponsorSkipNotice {
|
||||
min-width: 350px;
|
||||
max-width: 50%;
|
||||
|
||||
border-radius: 5px;
|
||||
|
||||
border-spacing: 5px 10px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
|
@ -149,17 +148,34 @@
|
|||
border-collapse: unset;
|
||||
}
|
||||
|
||||
.sponsorSkipNotice {
|
||||
min-width: 350px;
|
||||
background-color: rgba(28, 28, 28, 0.9);
|
||||
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sponsorSkipNotice .hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* For Cloudtube */
|
||||
.sponsorSkipNotice td, .sponsorSkipNotice table, .sponsorSkipNotice th {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeFadeIn {
|
||||
animation: fadeIn 0.5s;
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeFaded {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeFadeOut {
|
||||
animation: fadeOut 3s cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
transition: opacity 3s cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
opacity: 0 !important;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
.sponsorSkipNotice .sponsorSkipNoticeTimeLeft {
|
||||
|
@ -169,14 +185,28 @@
|
|||
padding: 2px 5px;
|
||||
font-size: 12px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
border: 1px solid #eeeeee;
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeTimeLeft img {
|
||||
vertical-align: middle;
|
||||
height: 13px;
|
||||
|
||||
padding-top: 7.8%;
|
||||
padding-bottom: 7.8%;
|
||||
}
|
||||
|
||||
/* if two are very close to eachother */
|
||||
.secondSkipNotice {
|
||||
bottom: 250px;
|
||||
}
|
||||
|
||||
transition: bottom 0.2s;
|
||||
.noticeLeftIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sponsorSkipNotice .sponsorSkipNoticeUnskipSection {
|
||||
|
@ -220,7 +250,9 @@
|
|||
|
||||
float: right;
|
||||
|
||||
margin-right: 5px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeRightButton {
|
||||
|
@ -243,7 +275,9 @@
|
|||
font-weight: bold;
|
||||
color: rgb(235, 235, 235);
|
||||
|
||||
margin-top: auto;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sponsorSkipInfo {
|
||||
|
@ -468,3 +502,20 @@ input::-webkit-inner-spin-button {
|
|||
height: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.skipButtonControlBarContainer {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.skipButtonControlBarContainer.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#sbSkipIconControlBarImage {
|
||||
height: 60%;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
|
@ -22,13 +22,13 @@
|
|||
Thanks for installing SponsorBlock. By using this extension, you agree to the <a href="https://gist.github.com/ajayyy/aa9f8ded2b573d4f73a3ffa0ef74f796">Privacy Policy</a> and <a href="https://gist.github.com/ajayyy/9e8100f069348e0bc062641f34d6af12">Terms of Use</a>.
|
||||
</p>
|
||||
|
||||
<p class="projectPreview">
|
||||
<p>
|
||||
Come contribute, make some suggestions and help out on <a href="https://discord.gg/QnmVMpU">Discord</a> or on <a href="https://matrix.to/#/#sponsor:ajay.app?via=ajay.app&via=matrix.org&via=mozilla.org">Matrix</a>.
|
||||
</p>
|
||||
|
||||
<p style="margin-bottom: 0" class="bigText center">Please review the options below</p>
|
||||
<p style="margin-bottom: 0; margin-top: 0" class="bigText center">Please review the options below</p>
|
||||
|
||||
<p>
|
||||
<p class="smallText">
|
||||
Many features are disabled by default. If you want to skip intros, outros, use Invidious, etc., enable them below.
|
||||
You can also hide/show UI elements.
|
||||
</p>
|
||||
|
@ -88,7 +88,7 @@
|
|||
<h1>Can I get a copy of the Database? What happens if you disappear?</h1>
|
||||
|
||||
<p>
|
||||
The database is public and available at <a href="https://sponsor.ajay.app/database">https://sponsor.ajay.app/database</a> and the source code is freely available. So, even if something happens to me, your submissions are not lost.
|
||||
The database is public and available at <a href="https://sponsor.ajay.app/database">https://sponsor.ajay.app/database</a>. The source code is freely available. So, even if something happens to me, your submissions are not lost.
|
||||
</p>
|
||||
|
||||
<h1>News and how it is made</h1>
|
||||
|
|
|
@ -129,8 +129,11 @@ a {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
p,li {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p,li,a {
|
||||
font-size: 20px;
|
||||
color: #c4c4c4;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,12 +41,13 @@
|
|||
id="namedview18"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.83098592"
|
||||
inkscape:cx="-238.41697"
|
||||
inkscape:cy="258.22009"
|
||||
inkscape:cx="220.07455"
|
||||
inkscape:cy="308.76246"
|
||||
inkscape:window-x="477"
|
||||
inkscape:window-y="961"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg16" />
|
||||
inkscape:current-layer="svg16"
|
||||
inkscape:pagecheckerboard="true" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
|
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3.1 KiB |
59
public/icons/pause.svg
Normal file
59
public/icons/pause.svg
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
width="24px"
|
||||
fill="#000000"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="pause.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<metadata
|
||||
id="metadata12">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="730"
|
||||
inkscape:window-height="480"
|
||||
id="namedview8"
|
||||
showgrid="false"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="12"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg6" />
|
||||
<path
|
||||
d="M0 0h24v24H0z"
|
||||
fill="none"
|
||||
id="path2" />
|
||||
<path
|
||||
d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"
|
||||
id="path4"
|
||||
style="fill:#ffffff" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
71
public/icons/skipIcon.svg
Normal file
71
public/icons/skipIcon.svg
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 565.15 568"
|
||||
version="1.1"
|
||||
id="svg16"
|
||||
sodipodi:docname="skipIcon.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
inkscape:export-filename="D:\Dell Data\_Projects\_____SponsorSkip\ignored\svg\SponsorBlocker4.png"
|
||||
inkscape:export-xdpi="43.436523"
|
||||
inkscape:export-ydpi="43.436523">
|
||||
<metadata
|
||||
id="metadata20">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>LogoSponsorBlocker2</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
id="namedview18"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.6619718"
|
||||
inkscape:cx="316.31071"
|
||||
inkscape:cy="330.01409"
|
||||
inkscape:window-x="477"
|
||||
inkscape:window-y="961"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg16"
|
||||
inkscape:pagecheckerboard="true" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
id="style2">.cls-1{fill:red;}.cls-2{fill:#fff;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title6">LogoSponsorBlocker2</title>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="m 282.58,568 a 65,65 0 0 1 -34.14,-9.66 C 95.41,463.94 2.54,300.46 0,121 a 64.91,64.91 0 0 1 34,-58.09 522.56,522.56 0 0 1 497.16,0 64.91,64.91 0 0 1 34,58.12 c -2.53,179.43 -95.4,342.91 -248.42,437.3 A 65,65 0 0 1 282.58,568 Z m 0,-548.31 A 502.24,502.24 0 0 0 43.4,80.22 45.27,45.27 0 0 0 19.7,120.75 c 2.44,172.67 91.81,330 239.07,420.83 a 46.19,46.19 0 0 0 47.61,0 C 453.64,450.73 543,293.42 545.45,120.75 A 45.26,45.26 0 0 0 521.75,80.21 502.26,502.26 0 0 0 282.58,19.69 Z"
|
||||
id="path8"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="M 284.70508 42.693359 A 479.9 479.9 0 0 0 54.369141 100.41992 A 22.53 22.53 0 0 0 42.669922 120.41992 C 45.069922 290.25992 135.67008 438.63977 270.83008 522.00977 A 22.48 22.48 0 0 0 294.32031 522.00977 C 429.48031 438.63977 520.08047 290.25992 522.48047 120.41992 A 22.53 22.53 0 0 0 510.7793 100.41992 A 479.9 479.9 0 0 0 284.70508 42.693359 z M 188.7168 140.07227 L 312.64844 264.00586 L 188.7168 387.9375 L 159.5918 358.8125 L 254.19336 264.00586 L 159.5918 169.19727 L 188.7168 140.07227 z M 305.625 140.07227 L 429.55859 264.00586 L 305.625 387.9375 L 276.50195 358.8125 L 371.10352 264.00586 L 276.50195 169.19727 L 305.625 140.07227 z "
|
||||
id="path10" />
|
||||
<g
|
||||
id="g825"
|
||||
transform="translate(-3.86549,36.564644)" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
59
public/icons/stop.svg
Normal file
59
public/icons/stop.svg
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
width="24px"
|
||||
fill="#000000"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="stop.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<metadata
|
||||
id="metadata12">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="730"
|
||||
inkscape:window-height="480"
|
||||
id="namedview8"
|
||||
showgrid="false"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="12"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg6" />
|
||||
<path
|
||||
d="M0 0h24v24H0z"
|
||||
fill="none"
|
||||
id="path2" />
|
||||
<path
|
||||
d="M6 6h12v12H6z"
|
||||
id="path4"
|
||||
style="fill:#ffffff" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -343,6 +343,9 @@ svg {
|
|||
|
||||
.categoryTableElement > * {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.categoryTableDescription > * {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ chrome.tabs.onUpdated.addListener(function(tabId) {
|
|||
chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
||||
switch(request.message) {
|
||||
case "openConfig":
|
||||
chrome.runtime.openOptionsPage();
|
||||
chrome.tabs.create({url: chrome.runtime.getURL('options/options.html' + (request.hash ? '#' + request.hash : ''))});
|
||||
return;
|
||||
case "openHelp":
|
||||
chrome.tabs.create({url: chrome.runtime.getURL('help/index_en.html')});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import * as React from "react";
|
||||
|
||||
import * as CompileConfig from "../../config.json";
|
||||
import { Category } from "../types";
|
||||
import CategorySkipOptionsComponent from "./CategorySkipOptionsComponent";
|
||||
|
||||
export interface CategoryChooserProps {
|
||||
|
@ -61,7 +62,7 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
|
|||
|
||||
for (const category of CompileConfig.categoryList) {
|
||||
elements.push(
|
||||
<CategorySkipOptionsComponent category={category}
|
||||
<CategorySkipOptionsComponent category={category as Category}
|
||||
key={category}>
|
||||
</CategorySkipOptionsComponent>
|
||||
);
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import * as React from "react";
|
||||
|
||||
import Config from "../config"
|
||||
import { CategorySkipOption } from "../types";
|
||||
import { Category, CategorySkipOption } from "../types";
|
||||
|
||||
import Utils from "../utils";
|
||||
import { getCategoryActionType } from "../utils/categoryUtils";
|
||||
const utils = new Utils();
|
||||
|
||||
export interface CategorySkipOptionsProps {
|
||||
category: string;
|
||||
category: Category;
|
||||
defaultColor?: string;
|
||||
defaultPreviewColor?: string;
|
||||
}
|
||||
|
@ -87,7 +91,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||
</tr>
|
||||
|
||||
<tr id={this.props.category + "DescriptionRow"}
|
||||
className="small-description">
|
||||
className="small-description categoryTableDescription">
|
||||
<td
|
||||
colSpan={2}>
|
||||
{chrome.i18n.getMessage("category_" + this.props.category + "_description")}
|
||||
|
@ -149,10 +153,13 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||
|
||||
const optionNames = ["disable", "showOverlay", "manualSkip", "autoSkip"];
|
||||
|
||||
console.log(getCategoryActionType(this.props.category))
|
||||
|
||||
for (const optionName of optionNames) {
|
||||
elements.push(
|
||||
<option key={optionName} value={optionName}>
|
||||
{chrome.i18n.getMessage(optionName)}
|
||||
{chrome.i18n.getMessage(optionName !== "disable" ? optionName + getCategoryActionType(this.props.category)
|
||||
: optionName)}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,20 +1,34 @@
|
|||
import * as React from "react";
|
||||
import Config from "../config";
|
||||
|
||||
enum CountdownMode {
|
||||
Timer,
|
||||
Paused,
|
||||
Stopped
|
||||
}
|
||||
|
||||
export interface NoticeProps {
|
||||
noticeTitle: string,
|
||||
|
||||
maxCountdownTime?: () => number,
|
||||
amountOfPreviousNotices?: number,
|
||||
showInSecondSlot?: boolean,
|
||||
timed?: boolean,
|
||||
idSuffix?: string,
|
||||
|
||||
videoSpeed?: () => number,
|
||||
|
||||
fadeIn?: boolean,
|
||||
startFaded?: boolean,
|
||||
firstColumn?: React.ReactElement,
|
||||
firstRow?: React.ReactElement,
|
||||
bottomRow?: React.ReactElement[],
|
||||
|
||||
smaller?: boolean,
|
||||
|
||||
// Callback for when this is closed
|
||||
closeListener: () => void,
|
||||
onMouseEnter?: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void,
|
||||
|
||||
zIndex?: number,
|
||||
style?: React.CSSProperties
|
||||
|
@ -26,8 +40,11 @@ export interface NoticeState {
|
|||
maxCountdownTime: () => number,
|
||||
|
||||
countdownTime: number,
|
||||
countdownText: string,
|
||||
countdownManuallyPaused: boolean,
|
||||
countdownMode: CountdownMode,
|
||||
|
||||
mouseHovering: boolean;
|
||||
|
||||
startFaded: boolean;
|
||||
}
|
||||
|
||||
class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
||||
|
@ -61,8 +78,10 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
|
||||
//the countdown until this notice closes
|
||||
countdownTime: maxCountdownTime(),
|
||||
countdownText: null,
|
||||
countdownManuallyPaused: false
|
||||
countdownMode: CountdownMode.Timer,
|
||||
mouseHovering: false,
|
||||
|
||||
startFaded: this.props.startFaded ?? false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,80 +96,149 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
}
|
||||
|
||||
return (
|
||||
<table id={"sponsorSkipNotice" + this.idSuffix}
|
||||
className={"sponsorSkipObject sponsorSkipNotice"
|
||||
<div id={"sponsorSkipNotice" + this.idSuffix}
|
||||
className={"sponsorSkipObject sponsorSkipNoticeParent"
|
||||
+ (this.props.showInSecondSlot ? " secondSkipNotice" : "")}
|
||||
onMouseEnter={(e) => this.onMouseEnter(e) }
|
||||
onMouseLeave={() => this.timerMouseLeave()}
|
||||
style={noticeStyle} >
|
||||
<table className={"sponsorSkipObject sponsorSkipNotice"
|
||||
+ (this.props.fadeIn ? " sponsorSkipNoticeFadeIn" : "")
|
||||
+ (this.amountOfPreviousNotices > 0 ? " secondSkipNotice" : "")}
|
||||
style={noticeStyle}
|
||||
onMouseEnter={() => this.timerMouseEnter()}
|
||||
onMouseLeave={() => this.timerMouseLeave()}>
|
||||
<tbody>
|
||||
+ (this.state.startFaded ? " sponsorSkipNoticeFaded" : "") } >
|
||||
<tbody>
|
||||
|
||||
{/* First row */}
|
||||
<tr id={"sponsorSkipNoticeFirstRow" + this.idSuffix}>
|
||||
{/* Left column */}
|
||||
<td>
|
||||
{/* Logo */}
|
||||
<img id={"sponsorSkipLogo" + this.idSuffix}
|
||||
className="sponsorSkipLogo sponsorSkipObject"
|
||||
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||
</img>
|
||||
{/* First row */}
|
||||
<tr id={"sponsorSkipNoticeFirstRow" + this.idSuffix}>
|
||||
{/* Left column */}
|
||||
<td className="noticeLeftIcon">
|
||||
{/* Logo */}
|
||||
<img id={"sponsorSkipLogo" + this.idSuffix}
|
||||
className="sponsorSkipLogo sponsorSkipObject"
|
||||
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||
</img>
|
||||
|
||||
<span id={"sponsorSkipMessage" + this.idSuffix}
|
||||
style={{float: "left"}}
|
||||
className="sponsorSkipMessage sponsorSkipObject">
|
||||
<span id={"sponsorSkipMessage" + this.idSuffix}
|
||||
style={{float: "left"}}
|
||||
className="sponsorSkipMessage sponsorSkipObject">
|
||||
|
||||
{this.state.noticeTitle}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{/* Right column */}
|
||||
<td className="sponsorSkipNoticeRightSection"
|
||||
style={{top: "11px"}}>
|
||||
|
||||
{/* Time left */}
|
||||
{this.props.timed ? (
|
||||
<span id={"sponsorSkipNoticeTimeLeft" + this.idSuffix}
|
||||
onClick={() => this.toggleManualPause()}
|
||||
className="sponsorSkipObject sponsorSkipNoticeTimeLeft">
|
||||
|
||||
{this.state.countdownText || (this.state.countdownTime + "s")}
|
||||
{this.state.noticeTitle}
|
||||
</span>
|
||||
) : ""}
|
||||
|
||||
{this.props.firstColumn}
|
||||
</td>
|
||||
|
||||
{this.props.firstRow}
|
||||
|
||||
{/* Right column */}
|
||||
<td className="sponsorSkipNoticeRightSection"
|
||||
style={{top: "9.32px"}}>
|
||||
|
||||
{/* Time left */}
|
||||
{this.props.timed ? (
|
||||
<span id={"sponsorSkipNoticeTimeLeft" + this.idSuffix}
|
||||
onClick={() => this.toggleManualPause()}
|
||||
className="sponsorSkipObject sponsorSkipNoticeTimeLeft">
|
||||
|
||||
{this.getCountdownElements()}
|
||||
|
||||
</span>
|
||||
) : ""}
|
||||
|
||||
|
||||
{/* Close button */}
|
||||
<img src={chrome.extension.getURL("icons/close.png")}
|
||||
className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
|
||||
onClick={() => this.close()}>
|
||||
</img>
|
||||
</td>
|
||||
</tr>
|
||||
{/* Close button */}
|
||||
<img src={chrome.extension.getURL("icons/close.png")}
|
||||
className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
|
||||
onClick={() => this.close()}>
|
||||
</img>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{this.props.children}
|
||||
{this.props.children}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
{!this.props.smaller && this.props.bottomRow ?
|
||||
this.props.bottomRow
|
||||
: null}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{/* Add as a hidden table to keep the height constant */}
|
||||
{this.props.smaller && this.props.bottomRow ?
|
||||
<table style={{visibility: "hidden", paddingTop: "14px"}}>
|
||||
<tbody>
|
||||
{this.props.bottomRow}
|
||||
</tbody>
|
||||
</table>
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getCountdownElements(): React.ReactElement[] {
|
||||
return [(
|
||||
<span
|
||||
id={"skipNoticeTimerText" + this.idSuffix}
|
||||
key="skipNoticeTimerText"
|
||||
className={this.state.countdownMode !== CountdownMode.Timer ? "hidden" : ""} >
|
||||
{this.state.countdownTime + "s"}
|
||||
</span>
|
||||
),(
|
||||
<img
|
||||
id={"skipNoticeTimerPaused" + this.idSuffix}
|
||||
key="skipNoticeTimerPaused"
|
||||
className={this.state.countdownMode !== CountdownMode.Paused ? "hidden" : ""}
|
||||
src={chrome.runtime.getURL("icons/pause.svg")}
|
||||
alt={chrome.i18n.getMessage("paused")} />
|
||||
),(
|
||||
<img
|
||||
id={"skipNoticeTimerStopped" + this.idSuffix}
|
||||
key="skipNoticeTimerStopped"
|
||||
className={this.state.countdownMode !== CountdownMode.Stopped ? "hidden" : ""}
|
||||
src={chrome.runtime.getURL("icons/stop.svg")}
|
||||
alt={chrome.i18n.getMessage("manualPaused")} />
|
||||
)];
|
||||
}
|
||||
|
||||
onMouseEnter(event: React.MouseEvent<HTMLElement, MouseEvent>): void {
|
||||
if (this.props.onMouseEnter) this.props.onMouseEnter(event);
|
||||
|
||||
this.fadedMouseEnter();
|
||||
this.timerMouseEnter();
|
||||
}
|
||||
|
||||
fadedMouseEnter(): void {
|
||||
if (this.state.startFaded) {
|
||||
this.setState({
|
||||
startFaded: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
timerMouseEnter(): void {
|
||||
if (this.state.countdownManuallyPaused) return;
|
||||
if (this.state.countdownMode === CountdownMode.Stopped) return;
|
||||
|
||||
this.pauseCountdown();
|
||||
|
||||
this.setState({
|
||||
mouseHovering: true
|
||||
});
|
||||
}
|
||||
|
||||
timerMouseLeave(): void {
|
||||
if (this.state.countdownManuallyPaused) return;
|
||||
if (this.state.countdownMode === CountdownMode.Stopped) return;
|
||||
|
||||
this.startCountdown();
|
||||
|
||||
this.setState({
|
||||
mouseHovering: false
|
||||
});
|
||||
}
|
||||
|
||||
toggleManualPause(): void {
|
||||
this.setState({
|
||||
countdownManuallyPaused: !this.state.countdownManuallyPaused
|
||||
countdownMode: this.state.countdownMode === CountdownMode.Stopped ? CountdownMode.Timer : CountdownMode.Stopped
|
||||
}, () => {
|
||||
if (this.state.countdownManuallyPaused) {
|
||||
if (this.state.countdownMode === CountdownMode.Stopped || this.state.mouseHovering) {
|
||||
this.pauseCountdown();
|
||||
} else {
|
||||
this.startCountdown();
|
||||
|
@ -207,7 +295,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
//reset countdown and inform the user
|
||||
this.setState({
|
||||
countdownTime: this.state.maxCountdownTime(),
|
||||
countdownText: this.state.countdownManuallyPaused ? chrome.i18n.getMessage("manualPaused") : chrome.i18n.getMessage("paused")
|
||||
countdownMode: this.state.countdownMode === CountdownMode.Timer ? CountdownMode.Paused : this.state.countdownMode
|
||||
});
|
||||
|
||||
this.removeFadeAnimation();
|
||||
|
@ -221,7 +309,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
|
||||
this.setState({
|
||||
countdownTime: this.state.maxCountdownTime(),
|
||||
countdownText: null
|
||||
countdownMode: CountdownMode.Timer
|
||||
});
|
||||
|
||||
this.setupInterval();
|
||||
|
@ -243,7 +331,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||
|
||||
this.setState({
|
||||
countdownTime: this.state.maxCountdownTime(),
|
||||
countdownText: null
|
||||
countdownMode: CountdownMode.Timer
|
||||
});
|
||||
|
||||
this.removeFadeAnimation();
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import * as React from "react";
|
||||
import * as CompileConfig from "../../config.json";
|
||||
import Config from "../config"
|
||||
import { ContentContainer, SponsorHideType, SponsorTime } from "../types";
|
||||
import { Category, ContentContainer, CategoryActionType, SponsorHideType, SponsorTime } from "../types";
|
||||
import NoticeComponent from "./NoticeComponent";
|
||||
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||
|
||||
import { getCategoryActionType, getSkippingText } from "../utils/categoryUtils";
|
||||
|
||||
export enum SkipNoticeAction {
|
||||
None,
|
||||
Upvote,
|
||||
|
@ -20,7 +22,11 @@ export interface SkipNoticeProps {
|
|||
// Contains functions and variables from the content script needed by the skip notice
|
||||
contentContainer: ContentContainer;
|
||||
|
||||
closeListener: () => void
|
||||
closeListener: () => void;
|
||||
showKeybindHint?: boolean;
|
||||
smaller: boolean;
|
||||
|
||||
unskipTime?: number;
|
||||
}
|
||||
|
||||
export interface SkipNoticeState {
|
||||
|
@ -41,6 +47,10 @@ export interface SkipNoticeState {
|
|||
thanksForVotingText?: string; //null until the voting buttons should be hidden
|
||||
|
||||
actionState?: SkipNoticeAction;
|
||||
|
||||
showKeybindHint?: boolean;
|
||||
|
||||
smaller?: boolean;
|
||||
}
|
||||
|
||||
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
|
||||
|
@ -50,6 +60,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
contentContainer: ContentContainer;
|
||||
|
||||
amountOfPreviousNotices: number;
|
||||
showInSecondSlot: boolean;
|
||||
audio: HTMLAudioElement;
|
||||
|
||||
idSuffix: string;
|
||||
|
@ -70,15 +81,12 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
this.contentContainer = props.contentContainer;
|
||||
this.audio = null;
|
||||
|
||||
const categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments"
|
||||
: "category_" + this.segments[0].category + "_short") || chrome.i18n.getMessage("category_" + this.segments[0].category);
|
||||
let noticeTitle = categoryName + " " + chrome.i18n.getMessage("skipped");
|
||||
if (!this.autoSkip) {
|
||||
noticeTitle = chrome.i18n.getMessage("skip_category").replace("{0}", categoryName);
|
||||
}
|
||||
const noticeTitle = getSkippingText(this.segments, this.props.autoSkip);
|
||||
|
||||
//add notice
|
||||
this.amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
||||
const previousSkipNotices = document.getElementsByClassName("sponsorSkipNoticeParent");
|
||||
this.amountOfPreviousNotices = previousSkipNotices.length;
|
||||
// If there is at least one already in the first slot
|
||||
this.showInSecondSlot = previousSkipNotices.length > 0 && [...previousSkipNotices].some(notice => !notice.classList.contains("secondSkipNotice"));
|
||||
|
||||
// Sort segments
|
||||
if (this.segments.length > 1) {
|
||||
|
@ -109,7 +117,11 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
choosingCategory: false,
|
||||
thanksForVotingText: null,
|
||||
|
||||
actionState: SkipNoticeAction.None
|
||||
actionState: SkipNoticeAction.None,
|
||||
|
||||
showKeybindHint: this.props.showKeybindHint ?? true,
|
||||
|
||||
smaller: this.props.smaller ?? false
|
||||
}
|
||||
|
||||
if (!this.autoSkip) {
|
||||
|
@ -132,145 +144,172 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
noticeStyle.transform = "scale(0.8) translate(10%, 10%)";
|
||||
}
|
||||
|
||||
// If it started out as smaller, always keep the
|
||||
// skip button there
|
||||
const firstColumn = this.props.smaller ? (
|
||||
this.getSkipButton()
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<NoticeComponent noticeTitle={this.state.noticeTitle}
|
||||
amountOfPreviousNotices={this.amountOfPreviousNotices}
|
||||
showInSecondSlot={this.showInSecondSlot}
|
||||
idSuffix={this.idSuffix}
|
||||
fadeIn={true}
|
||||
startFaded={false}
|
||||
timed={true}
|
||||
maxCountdownTime={this.state.maxCountdownTime}
|
||||
videoSpeed={() => this.contentContainer().v?.playbackRate}
|
||||
style={noticeStyle}
|
||||
ref={this.noticeRef}
|
||||
closeListener={() => this.closeListener()}>
|
||||
closeListener={() => this.closeListener()}
|
||||
smaller={this.state.smaller}
|
||||
firstColumn={firstColumn}
|
||||
bottomRow={[...this.getMessageBoxes(), ...this.getBottomRow() ]}
|
||||
onMouseEnter={() => this.onMouseEnter() } >
|
||||
|
||||
{(Config.config.audioNotificationOnSkip) && <audio ref={(source) => { this.audio = source; }}>
|
||||
<source src={chrome.extension.getURL("icons/beep.ogg")} type="audio/ogg"></source>
|
||||
</audio>}
|
||||
</NoticeComponent>
|
||||
);
|
||||
}
|
||||
|
||||
{/* Text Boxes */}
|
||||
{this.getMessageBoxes()}
|
||||
getBottomRow(): JSX.Element[] {
|
||||
return [
|
||||
/* Bottom Row */
|
||||
(<tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}
|
||||
key={0}>
|
||||
|
||||
{/* Bottom Row */}
|
||||
<tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}>
|
||||
{/* Vote Button Container */}
|
||||
{!this.state.thanksForVotingText ?
|
||||
<td id={"sponsorTimesVoteButtonsContainer" + this.idSuffix}
|
||||
className="sponsorTimesVoteButtonsContainer">
|
||||
|
||||
{/* Vote Button Container */}
|
||||
{!this.state.thanksForVotingText ?
|
||||
<td id={"sponsorTimesVoteButtonsContainer" + this.idSuffix}
|
||||
className="sponsorTimesVoteButtonsContainer">
|
||||
{/* Upvote Button */}
|
||||
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
|
||||
className="sponsorSkipObject voteButton"
|
||||
style={{marginRight: "10px"}}
|
||||
src={chrome.extension.getURL("icons/thumbs_up.svg")}
|
||||
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
||||
onClick={() => this.prepAction(SkipNoticeAction.Upvote)}>
|
||||
|
||||
{/* Upvote Button */}
|
||||
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
|
||||
className="sponsorSkipObject voteButton"
|
||||
style={{marginRight: "10px"}}
|
||||
src={chrome.extension.getURL("icons/thumbs_up.svg")}
|
||||
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
||||
onClick={() => this.prepAction(SkipNoticeAction.Upvote)}>
|
||||
</img>
|
||||
|
||||
</img>
|
||||
{/* Report Button */}
|
||||
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
|
||||
className="sponsorSkipObject voteButton"
|
||||
src={chrome.extension.getURL("icons/thumbs_down.svg")}
|
||||
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||
onClick={() => this.adjustDownvotingState(true)}>
|
||||
|
||||
{/* Report Button */}
|
||||
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
|
||||
className="sponsorSkipObject voteButton"
|
||||
src={chrome.extension.getURL("icons/thumbs_down.svg")}
|
||||
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||
onClick={() => this.adjustDownvotingState(true)}>
|
||||
</img>
|
||||
|
||||
</img>
|
||||
</td>
|
||||
|
||||
</td>
|
||||
:
|
||||
|
||||
:
|
||||
<td id={"sponsorTimesVoteButtonInfoMessage" + this.idSuffix}
|
||||
className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage"
|
||||
style={{marginRight: "10px"}}>
|
||||
{this.state.thanksForVotingText}
|
||||
</td>
|
||||
}
|
||||
|
||||
<td id={"sponsorTimesVoteButtonInfoMessage" + this.idSuffix}
|
||||
className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage"
|
||||
style={{marginRight: "10px"}}>
|
||||
{this.state.thanksForVotingText}
|
||||
</td>
|
||||
}
|
||||
{/* Unskip/Skip Button */}
|
||||
{!this.props.smaller ? this.getSkipButton() : null}
|
||||
|
||||
{/* Unskip Button */}
|
||||
<td className="sponsorSkipNoticeUnskipSection">
|
||||
<button id={"sponsorSkipUnskipButton" + this.idSuffix}
|
||||
className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||
style={{marginLeft: "4px"}}
|
||||
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
|
||||
{/* Never show button if autoSkip is enabled */}
|
||||
{!this.autoSkip ? "" :
|
||||
<td className="sponsorSkipNoticeRightSection"
|
||||
key={1}>
|
||||
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||
onClick={this.contentContainer().dontShowNoticeAgain}>
|
||||
|
||||
{this.state.unskipText + " (" + Config.config.skipKeybind + ")"}
|
||||
{chrome.i18n.getMessage("Hide")}
|
||||
</button>
|
||||
</td>
|
||||
}
|
||||
</tr>),
|
||||
|
||||
/* Downvote Options Row */
|
||||
(this.state.downvoting &&
|
||||
<tr id={"sponsorSkipNoticeDownvoteOptionsRow" + this.idSuffix}
|
||||
key={2}>
|
||||
<td id={"sponsorTimesDownvoteOptionsContainer" + this.idSuffix}>
|
||||
|
||||
{/* Normal downvote */}
|
||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||
onClick={() => this.prepAction(SkipNoticeAction.Downvote)}>
|
||||
{chrome.i18n.getMessage("downvoteDescription")}
|
||||
</button>
|
||||
|
||||
{/* Category vote */}
|
||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||
onClick={() => this.openCategoryChooser()}>
|
||||
|
||||
{chrome.i18n.getMessage("incorrectCategory")}
|
||||
</button>
|
||||
</td>
|
||||
|
||||
{/* Never show button if autoSkip is enabled */}
|
||||
{!this.autoSkip ? "" :
|
||||
<td className="sponsorSkipNoticeRightSection">
|
||||
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||
onClick={this.contentContainer().dontShowNoticeAgain}>
|
||||
|
||||
{chrome.i18n.getMessage("Hide")}
|
||||
</button>
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
),
|
||||
|
||||
{/* Downvote Options Row */}
|
||||
{this.state.downvoting &&
|
||||
<tr id={"sponsorSkipNoticeDownvoteOptionsRow" + this.idSuffix}>
|
||||
<td id={"sponsorTimesDownvoteOptionsContainer" + this.idSuffix}>
|
||||
/* Category Chooser Row */
|
||||
(this.state.choosingCategory &&
|
||||
<tr id={"sponsorSkipNoticeCategoryChooserRow" + this.idSuffix}
|
||||
key={3}>
|
||||
<td>
|
||||
{/* Category Selector */}
|
||||
<select id={"sponsorTimeCategories" + this.idSuffix}
|
||||
className="sponsorTimeCategories"
|
||||
defaultValue={this.segments[0].category} //Just default to the first segment, as we don't know which they'll choose
|
||||
ref={this.categoryOptionRef}>
|
||||
|
||||
{/* Normal downvote */}
|
||||
{this.getCategoryOptions()}
|
||||
</select>
|
||||
|
||||
{/* Submit Button */}
|
||||
{this.segments.length === 1 &&
|
||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||
onClick={() => this.prepAction(SkipNoticeAction.Downvote)}>
|
||||
{chrome.i18n.getMessage("downvoteDescription")}
|
||||
onClick={() => this.prepAction(SkipNoticeAction.CategoryVote)}>
|
||||
|
||||
{chrome.i18n.getMessage("submit")}
|
||||
</button>
|
||||
}
|
||||
|
||||
{/* Category vote */}
|
||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||
onClick={() => this.openCategoryChooser()}>
|
||||
</td>
|
||||
</tr>
|
||||
),
|
||||
|
||||
{chrome.i18n.getMessage("incorrectCategory")}
|
||||
</button>
|
||||
</td>
|
||||
/* Segment Chooser Row */
|
||||
(this.state.actionState !== SkipNoticeAction.None &&
|
||||
<tr id={"sponsorSkipNoticeSubmissionOptionsRow" + this.idSuffix}
|
||||
key={4}>
|
||||
<td id={"sponsorTimesSubmissionOptionsContainer" + this.idSuffix}>
|
||||
{this.getSubmissionChooser()}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
</tr>
|
||||
}
|
||||
getSkipButton(): JSX.Element {
|
||||
if (this.segments.length > 1
|
||||
|| getCategoryActionType(this.segments[0].category) !== CategoryActionType.POI
|
||||
|| this.props.unskipTime) {
|
||||
return (
|
||||
<span className="sponsorSkipNoticeUnskipSection">
|
||||
<button id={"sponsorSkipUnskipButton" + this.idSuffix}
|
||||
className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||
style={{marginLeft: "4px"}}
|
||||
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
|
||||
|
||||
{/* Category Chooser Row */}
|
||||
{this.state.choosingCategory &&
|
||||
<tr id={"sponsorSkipNoticeCategoryChooserRow" + this.idSuffix}>
|
||||
<td>
|
||||
{/* Category Selector */}
|
||||
<select id={"sponsorTimeCategories" + this.idSuffix}
|
||||
className="sponsorTimeCategories"
|
||||
defaultValue={this.segments[0].category} //Just default to the first segment, as we don't know which they'll choose
|
||||
ref={this.categoryOptionRef}>
|
||||
|
||||
{this.getCategoryOptions()}
|
||||
</select>
|
||||
|
||||
{/* Submit Button */}
|
||||
{this.segments.length === 1 &&
|
||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||
onClick={() => this.prepAction(SkipNoticeAction.CategoryVote)}>
|
||||
|
||||
{chrome.i18n.getMessage("submit")}
|
||||
</button>
|
||||
}
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
{/* Segment Chooser Row */}
|
||||
{this.state.actionState !== SkipNoticeAction.None &&
|
||||
<tr id={"sponsorSkipNoticeSubmissionOptionsRow" + this.idSuffix}>
|
||||
<td id={"sponsorTimesSubmissionOptionsContainer" + this.idSuffix}>
|
||||
{this.getSubmissionChooser()}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
</NoticeComponent>
|
||||
);
|
||||
{this.state.unskipText + (this.state.showKeybindHint ? " (" + Config.config.skipKeybind + ")" : "")}
|
||||
</button>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getSubmissionChooser(): JSX.Element[] {
|
||||
|
@ -289,6 +328,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
return elements;
|
||||
}
|
||||
|
||||
onMouseEnter(): void {
|
||||
if (this.state.smaller) {
|
||||
this.setState({
|
||||
smaller: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
prepAction(action: SkipNoticeAction): void {
|
||||
if (this.segments.length === 1) {
|
||||
this.performAction(0, action);
|
||||
|
@ -299,14 +346,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
}
|
||||
|
||||
getMessageBoxes(): JSX.Element[] | JSX.Element {
|
||||
getMessageBoxes(): JSX.Element[] {
|
||||
if (this.state.messages.length === 0) {
|
||||
// Add a spacer if there is no text
|
||||
return (
|
||||
return [
|
||||
<tr id={"sponsorSkipNoticeSpacer" + this.idSuffix}
|
||||
className="sponsorBlockSpacer">
|
||||
className="sponsorBlockSpacer"
|
||||
key={"messageBoxSpacer"}>
|
||||
</tr>
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
const elements: JSX.Element[] = [];
|
||||
|
@ -344,7 +392,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
this.contentContainer().vote(0, this.segments[index].UUID, undefined, this);
|
||||
break;
|
||||
case SkipNoticeAction.CategoryVote:
|
||||
this.contentContainer().vote(undefined, this.segments[index].UUID, this.categoryOptionRef.current.value, this)
|
||||
this.contentContainer().vote(undefined, this.segments[index].UUID, this.categoryOptionRef.current.value as Category, this)
|
||||
break;
|
||||
case SkipNoticeAction.Unskip:
|
||||
this.state.unskipCallback(index);
|
||||
|
@ -391,7 +439,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
getCategoryOptions(): React.ReactElement[] {
|
||||
const elements = [];
|
||||
|
||||
for (const category of CompileConfig.categoryList) {
|
||||
const categories = CompileConfig.categoryList.filter((cat => getCategoryActionType(cat as Category) === CategoryActionType.Skippable));
|
||||
for (const category of categories) {
|
||||
elements.push(
|
||||
<option value={category}
|
||||
key={category}>
|
||||
|
@ -404,7 +453,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
|
||||
unskip(index: number): void {
|
||||
this.contentContainer().unskipSponsorTime(this.segments[index]);
|
||||
this.contentContainer().unskipSponsorTime(this.segments[index], this.props.unskipTime);
|
||||
|
||||
this.unskippedMode(index, chrome.i18n.getMessage("reskip"));
|
||||
}
|
||||
|
@ -418,12 +467,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
}
|
||||
|
||||
getUnskippedModeInfo(index: number, buttonText: string): SkipNoticeState {
|
||||
const maxCountdownTime = () => {
|
||||
const changeCountdown = getCategoryActionType(this.segments[index].category) === CategoryActionType.Skippable;
|
||||
|
||||
const maxCountdownTime = changeCountdown ? () => {
|
||||
const sponsorTime = this.segments[index];
|
||||
const duration = Math.round((sponsorTime.segment[1] - this.contentContainer().v.currentTime) * (1 / this.contentContainer().v.playbackRate));
|
||||
|
||||
return Math.max(duration, Config.config.skipNoticeDuration);
|
||||
};
|
||||
} : this.state.maxCountdownTime;
|
||||
|
||||
return {
|
||||
unskipText: buttonText,
|
||||
|
@ -456,7 +507,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||
});
|
||||
}
|
||||
|
||||
afterVote(segment: SponsorTime, type: number, category: string): void {
|
||||
afterVote(segment: SponsorTime, type: number, category: Category): void {
|
||||
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
|
||||
|
||||
if (type === 0) {
|
||||
|
|
|
@ -4,8 +4,9 @@ import Config from "../config";
|
|||
import * as CompileConfig from "../../config.json";
|
||||
|
||||
import Utils from "../utils";
|
||||
import { ContentContainer, SponsorTime } from "../types";
|
||||
import { Category, CategoryActionType, ContentContainer, SponsorTime } from "../types";
|
||||
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
|
||||
import { getCategoryActionType } from "../utils/categoryUtils";
|
||||
const utils = new Utils();
|
||||
|
||||
export interface SponsorTimeEditProps {
|
||||
|
@ -16,6 +17,7 @@ export interface SponsorTimeEditProps {
|
|||
contentContainer: ContentContainer,
|
||||
|
||||
submissionNotice: SubmissionNoticeComponent;
|
||||
categoryList?: Category[];
|
||||
}
|
||||
|
||||
export interface SponsorTimeEditState {
|
||||
|
@ -106,43 +108,47 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
onChange={(e) => {
|
||||
const sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||
sponsorTimeEdits[0] = e.target.value;
|
||||
if (getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTimeEdits[1] = e.target.value;
|
||||
|
||||
this.setState({sponsorTimeEdits});
|
||||
|
||||
this.saveEditTimes();
|
||||
}}>
|
||||
</input>
|
||||
|
||||
<span>
|
||||
{" " + chrome.i18n.getMessage("to") + " "}
|
||||
</span>
|
||||
{getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable ? (
|
||||
<span>
|
||||
<span>
|
||||
{" " + chrome.i18n.getMessage("to") + " "}
|
||||
</span>
|
||||
|
||||
<input id={"submittingTime1" + this.idSuffix}
|
||||
className="sponsorTimeEdit sponsorTimeEditInput"
|
||||
ref={oldYouTubeDarkStyles}
|
||||
type="text"
|
||||
value={this.state.sponsorTimeEdits[1]}
|
||||
onChange={(e) => {
|
||||
const sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||
sponsorTimeEdits[1] = e.target.value;
|
||||
<input id={"submittingTime1" + this.idSuffix}
|
||||
className="sponsorTimeEdit sponsorTimeEditInput"
|
||||
ref={oldYouTubeDarkStyles}
|
||||
type="text"
|
||||
value={this.state.sponsorTimeEdits[1]}
|
||||
onChange={(e) => {
|
||||
const sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||
sponsorTimeEdits[1] = e.target.value;
|
||||
|
||||
this.setState({sponsorTimeEdits});
|
||||
this.setState({sponsorTimeEdits});
|
||||
|
||||
this.saveEditTimes();
|
||||
}}>
|
||||
</input>
|
||||
this.saveEditTimes();
|
||||
}}>
|
||||
</input>
|
||||
|
||||
<span id={"nowButton1" + this.idSuffix}
|
||||
className="sponsorNowButton"
|
||||
onClick={() => this.setTimeToNow(1)}>
|
||||
{chrome.i18n.getMessage("bracketNow")}
|
||||
</span>
|
||||
<span id={"nowButton1" + this.idSuffix}
|
||||
className="sponsorNowButton"
|
||||
onClick={() => this.setTimeToNow(1)}>
|
||||
{chrome.i18n.getMessage("bracketNow")}
|
||||
</span>
|
||||
|
||||
<span id={"endButton" + this.idSuffix}
|
||||
className="sponsorNowButton"
|
||||
onClick={() => this.setTimeToEnd()}>
|
||||
{chrome.i18n.getMessage("bracketEnd")}
|
||||
</span>
|
||||
<span id={"endButton" + this.idSuffix}
|
||||
className="sponsorNowButton"
|
||||
onClick={() => this.setTimeToEnd()}>
|
||||
{chrome.i18n.getMessage("bracketEnd")}
|
||||
</span>
|
||||
</span>
|
||||
): ""}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
|
@ -151,7 +157,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
className="sponsorTimeDisplay"
|
||||
onClick={this.toggleEditTime.bind(this)}>
|
||||
{utils.getFormattedTime(segment[0], true) +
|
||||
((!isNaN(segment[1])) ? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")}
|
||||
((!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable)
|
||||
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -191,7 +198,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
{chrome.i18n.getMessage("delete")}
|
||||
</span>
|
||||
|
||||
{(!isNaN(segment[1])) ? (
|
||||
{(!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable) ? (
|
||||
<span id={"sponsorTimePreviewButton" + this.idSuffix}
|
||||
className="sponsorTimeEditButton"
|
||||
onClick={this.previewTime.bind(this)}>
|
||||
|
@ -226,7 +233,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
</option>
|
||||
)];
|
||||
|
||||
for (const category of CompileConfig.categoryList) {
|
||||
for (const category of (this.props.categoryList ?? CompileConfig.categoryList)) {
|
||||
elements.push(
|
||||
<option value={category}
|
||||
key={category}>
|
||||
|
@ -248,12 +255,17 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
if (confirm(chrome.i18n.getMessage("enableThisCategoryFirst")
|
||||
.replace("{0}", chrome.i18n.getMessage("category_" + chosenCategory)))) {
|
||||
// Open options page
|
||||
chrome.runtime.sendMessage({"message": "openConfig"});
|
||||
chrome.runtime.sendMessage({message: "openConfig", hash: chosenCategory + "OptionsName"});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (getCategoryActionType(event.target.value as Category) === CategoryActionType.POI) {
|
||||
this.setTimeTo(1, null);
|
||||
this.props.contentContainer().updateEditButtonsOnPlayer();
|
||||
}
|
||||
|
||||
this.saveEditTimes();
|
||||
}
|
||||
|
||||
|
@ -265,11 +277,16 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
this.setTimeTo(1, this.props.contentContainer().v.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* @param time If null, will set time to the first index's time
|
||||
*/
|
||||
setTimeTo(index: number, time: number): void {
|
||||
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||
if (time === null) time = sponsorTime.segment[0];
|
||||
|
||||
sponsorTime.segment[index] =
|
||||
time;
|
||||
sponsorTime.segment[index] = time;
|
||||
if (getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTime.segment[1] = time;
|
||||
|
||||
this.setState({
|
||||
sponsorTimeEdits: this.getFormattedSponsorTimesEdits(sponsorTime)
|
||||
|
@ -313,7 +330,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
}
|
||||
}
|
||||
|
||||
sponsorTimesSubmitting[this.props.index].category = this.categoryOptionRef.current.value;
|
||||
sponsorTimesSubmitting[this.props.index].category = this.categoryOptionRef.current.value as Category;
|
||||
|
||||
Config.config.segmentTimes.set(this.props.contentContainer().sponsorVideoID, sponsorTimesSubmitting);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ export interface SubmissionNoticeProps {
|
|||
|
||||
callback: () => unknown;
|
||||
|
||||
closeListener: () => void
|
||||
closeListener: () => void;
|
||||
}
|
||||
|
||||
export interface SubmissionNoticeeState {
|
||||
|
|
145
src/config.ts
145
src/config.ts
|
@ -1,14 +1,11 @@
|
|||
import * as CompileConfig from "../config.json";
|
||||
import { CategorySelection, CategorySkipOption, PreviewBarOption, SponsorTime, StorageChangesObject, UnEncodedSegmentTimes as UnencodedSegmentTimes } from "./types";
|
||||
|
||||
import Utils from "./utils";
|
||||
const utils = new Utils();
|
||||
import { Category, CategorySelection, CategorySkipOption, PreviewBarOption, SponsorTime, StorageChangesObject, UnEncodedSegmentTimes as UnencodedSegmentTimes } from "./types";
|
||||
|
||||
interface SBConfig {
|
||||
userID: string,
|
||||
/** Contains unsubmitted segments that the user has created. */
|
||||
segmentTimes: SBMap<string, SponsorTime[]>,
|
||||
defaultCategory: string,
|
||||
defaultCategory: Category,
|
||||
whitelistedChannels: string[],
|
||||
forceChannelCheck: boolean,
|
||||
skipKeybind: string,
|
||||
|
@ -63,6 +60,8 @@ interface SBConfig {
|
|||
"preview-preview": PreviewBarOption,
|
||||
"music_offtopic": PreviewBarOption,
|
||||
"preview-music_offtopic": PreviewBarOption,
|
||||
"poi_highlight": PreviewBarOption,
|
||||
"preview-poi_highlight": PreviewBarOption,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +147,7 @@ const Config: SBObject = {
|
|||
defaults: {
|
||||
userID: null,
|
||||
segmentTimes: new SBMap("segmentTimes"),
|
||||
defaultCategory: "chooseACategory",
|
||||
defaultCategory: "chooseACategory" as Category,
|
||||
whitelistedChannels: [],
|
||||
forceChannelCheck: false,
|
||||
skipKeybind: "Enter",
|
||||
|
@ -184,7 +183,7 @@ const Config: SBObject = {
|
|||
autoSkipOnMusicVideos: false,
|
||||
|
||||
categorySelections: [{
|
||||
name: "sponsor",
|
||||
name: "sponsor" as Category,
|
||||
option: CategorySkipOption.AutoSkip
|
||||
}],
|
||||
|
||||
|
@ -249,6 +248,14 @@ const Config: SBObject = {
|
|||
"preview-music_offtopic": {
|
||||
color: "#a6634a",
|
||||
opacity: "0.7"
|
||||
},
|
||||
"poi_highlight": {
|
||||
color: "#ff1684",
|
||||
opacity: "0.7"
|
||||
},
|
||||
"preview-poi_highlight": {
|
||||
color: "#9b044c",
|
||||
opacity: "0.7"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -345,6 +352,17 @@ function fetchConfig(): Promise<void> {
|
|||
}
|
||||
|
||||
function migrateOldFormats(config: SBConfig) {
|
||||
if (!config["highlightCategoryAdded"] && !config.categorySelections.some((s) => s.name === "poi_highlight")) {
|
||||
config["highlightCategoryAdded"] = true;
|
||||
|
||||
config.categorySelections.push({
|
||||
name: "poi_highlight" as Category,
|
||||
option: CategorySkipOption.ManualSkip
|
||||
});
|
||||
|
||||
config.categorySelections = config.categorySelections;
|
||||
}
|
||||
|
||||
if (config["askAboutUnlistedVideos"]) {
|
||||
chrome.storage.sync.remove("askAboutUnlistedVideos");
|
||||
}
|
||||
|
@ -361,25 +379,6 @@ function migrateOldFormats(config: SBConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
// Adding preview category
|
||||
if (!config["previewCategoryUpdate"]) {
|
||||
config["previewCategoryUpdate"] = true;
|
||||
for (const selection of config.categorySelections) {
|
||||
if (selection.name === "intro"
|
||||
&& selection.option === CategorySkipOption.AutoSkip || selection.option === CategorySkipOption.ManualSkip) {
|
||||
|
||||
// Add a default skip option for preview category
|
||||
config.categorySelections.push({
|
||||
name: "preview",
|
||||
option: CategorySkipOption.ManualSkip
|
||||
});
|
||||
// Ensure it gets updated
|
||||
config.categorySelections = config.categorySelections;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config["disableAutoSkip"]) {
|
||||
for (const selection of config.categorySelections) {
|
||||
if (selection.name === "sponsor") {
|
||||
|
@ -390,100 +389,6 @@ function migrateOldFormats(config: SBConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
// Auto vote removal
|
||||
if (config["autoUpvote"]) {
|
||||
chrome.storage.sync.remove("autoUpvote");
|
||||
}
|
||||
// mobileUpdateShowCount removal
|
||||
if (config["mobileUpdateShowCount"] !== undefined) {
|
||||
chrome.storage.sync.remove("mobileUpdateShowCount");
|
||||
}
|
||||
// categoryUpdateShowCount removal
|
||||
if (config["categoryUpdateShowCount"] !== undefined) {
|
||||
chrome.storage.sync.remove("categoryUpdateShowCount");
|
||||
}
|
||||
|
||||
// Channel URLS
|
||||
if (config.whitelistedChannels.length > 0 &&
|
||||
(config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) {
|
||||
const channelURLFixer = async() => {
|
||||
const newChannelList: string[] = [];
|
||||
for (const item of config.whitelistedChannels) {
|
||||
if (item != null) {
|
||||
if (item.includes("/channel/")) {
|
||||
newChannelList.push(item.split("/")[2]);
|
||||
} else if (item.includes("/user/") && utils.isContentScript()) {
|
||||
|
||||
|
||||
// Replace channel URL with channelID
|
||||
const response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
|
||||
|
||||
if (response.ok) {
|
||||
newChannelList.push((JSON.parse(response.responseText)).authorId);
|
||||
} else {
|
||||
// Add it at the beginning so it gets converted later
|
||||
newChannelList.unshift(item);
|
||||
}
|
||||
} else if (item.includes("/user/")) {
|
||||
// Add it at the beginning so it gets converted later (The API can only be called in the content script due to CORS issues)
|
||||
newChannelList.unshift(item);
|
||||
} else {
|
||||
newChannelList.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.whitelistedChannels = newChannelList;
|
||||
}
|
||||
|
||||
channelURLFixer();
|
||||
}
|
||||
|
||||
// Check if off-topic category needs to be removed
|
||||
for (let i = 0; i < config.categorySelections.length; i++) {
|
||||
if (config.categorySelections[i].name === "offtopic") {
|
||||
config.categorySelections.splice(i, 1);
|
||||
// Call set listener
|
||||
config.categorySelections = config.categorySelections;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate old "sponsorTimes"
|
||||
if (config["sponsorTimes"]) {
|
||||
let jsonData: unknown = config["sponsorTimes"];
|
||||
|
||||
// Check if data is stored in the old format for SBMap (a JSON string)
|
||||
if (typeof jsonData === "string") {
|
||||
try {
|
||||
jsonData = JSON.parse(jsonData);
|
||||
} catch(e) {
|
||||
// Continue normally (out of this if statement)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise junk data
|
||||
if (Array.isArray(jsonData)) {
|
||||
const oldMap = new Map(jsonData);
|
||||
oldMap.forEach((sponsorTimes: [number, number][], key) => {
|
||||
const segmentTimes: SponsorTime[] = [];
|
||||
for (const segment of sponsorTimes) {
|
||||
segmentTimes.push({
|
||||
segment: segment,
|
||||
category: "sponsor",
|
||||
UUID: null
|
||||
});
|
||||
}
|
||||
|
||||
config.segmentTimes.rawSet(key, segmentTimes);
|
||||
});
|
||||
|
||||
config.segmentTimes.update();
|
||||
}
|
||||
|
||||
chrome.storage.sync.remove("sponsorTimes");
|
||||
}
|
||||
|
||||
// Remove some old unused options
|
||||
if (config["sponsorVideoID"] !== undefined) {
|
||||
chrome.storage.sync.remove("sponsorVideoID");
|
||||
|
|
135
src/content.ts
135
src/content.ts
|
@ -1,6 +1,5 @@
|
|||
import Config from "./config";
|
||||
|
||||
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType } from "./types";
|
||||
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, CategoryActionType, ChannelIDInfo, ChannelIDStatus, SponsorSourceType, SegmentUUID, Category, SkipToTimeParams, ToggleSkippable } from "./types";
|
||||
|
||||
import { ContentContainer } from "./types";
|
||||
import Utils from "./utils";
|
||||
|
@ -14,6 +13,8 @@ import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
|||
import SubmissionNotice from "./render/SubmissionNotice";
|
||||
import { Message, MessageResponse } from "./messageTypes";
|
||||
import * as Chat from "./js-components/chat";
|
||||
import { getCategoryActionType } from "./utils/categoryUtils";
|
||||
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||
|
||||
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
||||
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
||||
|
@ -26,6 +27,7 @@ let sponsorTimes: SponsorTime[] = null;
|
|||
let sponsorVideoID: VideoID = null;
|
||||
// List of open skip notices
|
||||
const skipNotices: SkipNotice[] = [];
|
||||
let activeSkipKeybindElement: ToggleSkippable = null;
|
||||
|
||||
// JSON video info
|
||||
let videoInfo: VideoInfo = null;
|
||||
|
@ -69,6 +71,7 @@ let channelWhitelisted = false;
|
|||
|
||||
// create preview bar
|
||||
let previewBar: PreviewBar = null;
|
||||
let skipButtonControlBar: SkipButtonControlBar = null;
|
||||
|
||||
/** Element containing the player controls on the YouTube player. */
|
||||
let controls: HTMLElement | null = null;
|
||||
|
@ -449,7 +452,12 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||
if (incorrectVideoCheck(videoID, currentSkip)) return;
|
||||
|
||||
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
|
||||
skipToTime(video, skipTime, skippingSegments, skipInfo.openNotice);
|
||||
skipToTime({
|
||||
v: video,
|
||||
skipTime,
|
||||
skippingSegments,
|
||||
openNotice: skipInfo.openNotice
|
||||
});
|
||||
|
||||
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ManualSkip) {
|
||||
forcedSkipTime = skipTime[0] + 0.001;
|
||||
|
@ -511,6 +519,7 @@ function refreshVideoAttachments() {
|
|||
videosWithEventListeners.push(video);
|
||||
|
||||
setupVideoListeners();
|
||||
setupSkipButtonControlBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -562,6 +571,22 @@ function setupVideoListeners() {
|
|||
|
||||
startSponsorSchedule();
|
||||
}
|
||||
|
||||
if (!Config.config.dontShowNotice) {
|
||||
const currentPoiSegment = sponsorTimes.find((segment) =>
|
||||
getCategoryActionType(segment.category) === CategoryActionType.POI &&
|
||||
video.currentTime - segment.segment[0] > 0 &&
|
||||
video.currentTime - segment.segment[0] < video.duration * 0.006); // Approximate size on preview bar
|
||||
if (currentPoiSegment && !skipNotices.some((notice) => notice.segments.some((s) => s.UUID === currentPoiSegment.UUID))) {
|
||||
skipToTime({
|
||||
v: video,
|
||||
skipTime: currentPoiSegment.segment,
|
||||
skippingSegments: [currentPoiSegment],
|
||||
openNotice: true,
|
||||
forceAutoSkip: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
video.addEventListener('ratechange', () => startSponsorSchedule());
|
||||
// Used by videospeed extension (https://github.com/igrigorik/videospeed/pull/740)
|
||||
|
@ -578,6 +603,22 @@ function setupVideoListeners() {
|
|||
}
|
||||
}
|
||||
|
||||
function setupSkipButtonControlBar() {
|
||||
if (!skipButtonControlBar) {
|
||||
skipButtonControlBar = new SkipButtonControlBar({
|
||||
skip: (segment) => skipToTime({
|
||||
v: video,
|
||||
skipTime: segment.segment,
|
||||
skippingSegments: [segment],
|
||||
openNotice: true,
|
||||
forceAutoSkip: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
skipButtonControlBar.attachToPage();
|
||||
}
|
||||
|
||||
async function sponsorsLookup(id: string, keepOldSubmissions = true) {
|
||||
if (!video) refreshVideoAttachments();
|
||||
//there is still no video here
|
||||
|
@ -707,24 +748,47 @@ function retryFetch(): void {
|
|||
function startSkipScheduleCheckingForStartSponsors() {
|
||||
if (!switchingVideos) {
|
||||
// See if there are any starting sponsors
|
||||
let startingSponsor = -1;
|
||||
let startingSegmentTime = -1;
|
||||
let startingSegment: SponsorTime = null;
|
||||
for (const time of sponsorTimes) {
|
||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
|
||||
startingSponsor = time.segment[0];
|
||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
||||
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) {
|
||||
startingSegmentTime = time.segment[0];
|
||||
startingSegment = time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startingSponsor === -1) {
|
||||
if (startingSegmentTime === -1) {
|
||||
for (const time of sponsorTimesSubmitting) {
|
||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
|
||||
startingSponsor = time.segment[0];
|
||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
||||
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) {
|
||||
startingSegmentTime = time.segment[0];
|
||||
startingSegment = time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (startingSponsor !== -1) {
|
||||
startSponsorSchedule(undefined, startingSponsor);
|
||||
// For highlight category
|
||||
const poiSegments = sponsorTimes
|
||||
.filter((time) => time.segment[1] > video.currentTime && getCategoryActionType(time.category) === CategoryActionType.POI)
|
||||
.sort((a, b) => b.segment[0] - a.segment[0]);
|
||||
for (const time of poiSegments) {
|
||||
const skipOption = utils.getCategorySelection(time.category)?.option;
|
||||
if (skipOption !== CategorySkipOption.ShowOverlay) {
|
||||
skipToTime({
|
||||
v: video,
|
||||
skipTime: time.segment,
|
||||
skippingSegments: [time],
|
||||
openNotice: true,
|
||||
unskipTime: video.currentTime
|
||||
});
|
||||
if (skipOption === CategorySkipOption.AutoSkip) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startingSegmentTime !== -1) {
|
||||
startSponsorSchedule(undefined, startingSegmentTime);
|
||||
} else {
|
||||
startSponsorSchedule();
|
||||
}
|
||||
|
@ -811,7 +875,6 @@ function updatePreviewBar(): void {
|
|||
if (video === null) return;
|
||||
|
||||
const previewBarSegments: PreviewBarSegment[] = [];
|
||||
|
||||
if (sponsorTimes) {
|
||||
sponsorTimes.forEach((segment) => {
|
||||
if (segment.hidden !== SponsorHideType.Visible) return;
|
||||
|
@ -820,6 +883,7 @@ function updatePreviewBar(): void {
|
|||
segment: segment.segment as [number, number],
|
||||
category: segment.category,
|
||||
unsubmitted: false,
|
||||
showLarger: getCategoryActionType(segment.category) === CategoryActionType.POI
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -829,6 +893,7 @@ function updatePreviewBar(): void {
|
|||
segment: segment.segment as [number, number],
|
||||
category: segment.category,
|
||||
unsubmitted: true,
|
||||
showLarger: getCategoryActionType(segment.category) === CategoryActionType.POI
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -977,7 +1042,8 @@ function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments:
|
|||
|| ((includeNonIntersectingSegments && sponsorTimes[i].segment[0] >= minimum)
|
||||
|| (includeIntersectingSegments && sponsorTimes[i].segment[0] < minimum && sponsorTimes[i].segment[1] > minimum)))
|
||||
&& (!onlySkippableSponsors || utils.getCategorySelection(sponsorTimes[i].category).option !== CategorySkipOption.ShowOverlay)
|
||||
&& (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)) {
|
||||
&& (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)
|
||||
&& getCategoryActionType(sponsorTimes[i].category) === CategoryActionType.Skippable) {
|
||||
|
||||
startTimes.push(sponsorTimes[i].segment[0]);
|
||||
}
|
||||
|
@ -1021,9 +1087,9 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
|
|||
}
|
||||
|
||||
//skip from the start time to the end time for a certain index sponsor time
|
||||
function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: SponsorTime[], openNotice: boolean) {
|
||||
function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, unskipTime}: SkipToTimeParams): void {
|
||||
// There will only be one submission if it is manual skip
|
||||
const autoSkip: boolean = shouldAutoSkip(skippingSegments[0]);
|
||||
const autoSkip: boolean = forceAutoSkip || shouldAutoSkip(skippingSegments[0]);
|
||||
|
||||
if ((autoSkip || sponsorTimesSubmitting.includes(skippingSegments[0])) && v.currentTime !== skipTime[1]) {
|
||||
// Fix for looped videos not working when skipping to the end #426
|
||||
|
@ -1035,10 +1101,23 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S
|
|||
}
|
||||
}
|
||||
|
||||
if (openNotice) {
|
||||
//send out the message saying that a sponsor message was skipped
|
||||
if (!Config.config.dontShowNotice || !autoSkip) {
|
||||
skipNotices.push(new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer));
|
||||
if (!autoSkip
|
||||
&& skippingSegments.length === 1
|
||||
&& getCategoryActionType(skippingSegments[0].category) === CategoryActionType.POI) {
|
||||
skipButtonControlBar.enable(skippingSegments[0]);
|
||||
|
||||
activeSkipKeybindElement?.setShowKeybindHint(false);
|
||||
activeSkipKeybindElement = skipButtonControlBar;
|
||||
} else {
|
||||
if (openNotice) {
|
||||
//send out the message saying that a sponsor message was skipped
|
||||
if (!Config.config.dontShowNotice || !autoSkip) {
|
||||
const newSkipNotice = new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer, unskipTime);
|
||||
skipNotices.push(newSkipNotice);
|
||||
|
||||
activeSkipKeybindElement?.setShowKeybindHint(false);
|
||||
activeSkipKeybindElement = newSkipNotice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1046,11 +1125,10 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S
|
|||
if (autoSkip) sendTelemetryAndCount(skippingSegments, skipTime[1] - skipTime[0], true);
|
||||
}
|
||||
|
||||
function unskipSponsorTime(segment: SponsorTime) {
|
||||
if (sponsorTimes != null) {
|
||||
//add a tiny bit of time to make sure it is not skipped again
|
||||
video.currentTime = segment.segment[0] + 0.001;
|
||||
}
|
||||
function unskipSponsorTime(segment: SponsorTime, unskipTime: number = null) {
|
||||
//add a tiny bit of time to make sure it is not skipped again
|
||||
console.log(unskipTime)
|
||||
video.currentTime = unskipTime ?? segment.segment[0] + 0.001;
|
||||
}
|
||||
|
||||
function reskipSponsorTime(segment: SponsorTime) {
|
||||
|
@ -1189,7 +1267,7 @@ function updateEditButtonsOnPlayer(): void {
|
|||
creatingSegment = isSegmentCreationInProgress();
|
||||
|
||||
// Show only if there are any segments to submit
|
||||
submitButtonVisible = sponsorTimesSubmitting.length > 1 || (sponsorTimesSubmitting.length > 0 && !creatingSegment);
|
||||
submitButtonVisible = sponsorTimesSubmitting.length > 0;
|
||||
|
||||
// Show only if there are any segments to delete
|
||||
deleteButtonVisible = sponsorTimesSubmitting.length > 1 || (sponsorTimesSubmitting.length > 0 && !creatingSegment);
|
||||
|
@ -1427,7 +1505,7 @@ function clearSponsorTimes() {
|
|||
}
|
||||
|
||||
//if skipNotice is null, it will not affect the UI
|
||||
function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNoticeComponent) {
|
||||
function vote(type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent) {
|
||||
if (skipNotice !== null && skipNotice !== undefined) {
|
||||
//add loading info
|
||||
skipNotice.addVoteButtonInfo.bind(skipNotice)(chrome.i18n.getMessage("Loading"))
|
||||
|
@ -1631,9 +1709,8 @@ function hotkeyListener(e: KeyboardEvent): void {
|
|||
|
||||
switch (key) {
|
||||
case skipKey:
|
||||
if (skipNotices.length > 0) {
|
||||
const latestSkipNotice = skipNotices[skipNotices.length - 1];
|
||||
latestSkipNotice.toggleSkip.call(latestSkipNotice);
|
||||
if (activeSkipKeybindElement) {
|
||||
activeSkipKeybindElement.toggleSkip.call(activeSkipKeybindElement);
|
||||
}
|
||||
break;
|
||||
case startSponsorKey:
|
||||
|
|
|
@ -15,6 +15,7 @@ export interface PreviewBarSegment {
|
|||
segment: [number, number];
|
||||
category: string;
|
||||
unsubmitted: boolean;
|
||||
showLarger: boolean;
|
||||
}
|
||||
|
||||
class PreviewBar {
|
||||
|
@ -102,9 +103,10 @@ class PreviewBar {
|
|||
let currentSegmentLength = Infinity;
|
||||
|
||||
for (const seg of this.segments) {
|
||||
if (seg.segment[0] <= timeInSeconds && seg.segment[1] > timeInSeconds) {
|
||||
const segmentLength = seg.segment[1] - seg.segment[0];
|
||||
|
||||
const segmentLength = seg.segment[1] - seg.segment[0];
|
||||
const startTime = segmentLength !== 0 ? seg.segment[0] : Math.floor(seg.segment[0]);
|
||||
const endTime = segmentLength !== 0 ? seg.segment[1] : Math.ceil(seg.segment[1]);
|
||||
if (startTime <= timeInSeconds && endTime >= timeInSeconds) {
|
||||
if (segmentLength < currentSegmentLength) {
|
||||
currentSegmentLength = segmentLength;
|
||||
segment = seg;
|
||||
|
@ -181,10 +183,10 @@ class PreviewBar {
|
|||
});
|
||||
}
|
||||
|
||||
createBar({category, unsubmitted, segment}: PreviewBarSegment): HTMLLIElement {
|
||||
createBar({category, unsubmitted, segment, showLarger}: PreviewBarSegment): HTMLLIElement {
|
||||
const bar = document.createElement('li');
|
||||
bar.classList.add('previewbar');
|
||||
bar.innerHTML = ' ';
|
||||
bar.innerHTML = showLarger ? ' ' : ' ';
|
||||
|
||||
const fullCategoryName = (unsubmitted ? 'preview-' : '') + category;
|
||||
bar.setAttribute('sponsorblock-category', fullCategoryName);
|
||||
|
@ -193,7 +195,7 @@ class PreviewBar {
|
|||
if (!this.onMobileYouTube) bar.style.opacity = Config.config.barTypes[fullCategoryName]?.opacity;
|
||||
|
||||
bar.style.position = "absolute";
|
||||
bar.style.width = this.timeToPercentage(segment[1] - segment[0]);
|
||||
if (segment[1] - segment[0] > 0) bar.style.width = this.timeToPercentage(segment[1] - segment[0]);
|
||||
bar.style.left = this.timeToPercentage(segment[0]);
|
||||
|
||||
return bar;
|
||||
|
|
94
src/js-components/skipButtonControlBar.ts
Normal file
94
src/js-components/skipButtonControlBar.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
import Config from "../config";
|
||||
import { SponsorTime } from "../types";
|
||||
import { getSkippingText } from "../utils/categoryUtils";
|
||||
|
||||
|
||||
export interface SkipButtonControlBarProps {
|
||||
skip: (segment: SponsorTime) => void;
|
||||
}
|
||||
|
||||
export class SkipButtonControlBar {
|
||||
|
||||
container: HTMLElement;
|
||||
skipIcon: HTMLImageElement;
|
||||
textContainer: HTMLElement;
|
||||
chapterText: HTMLElement;
|
||||
segment: SponsorTime;
|
||||
|
||||
showKeybindHint = true;
|
||||
|
||||
timeout: NodeJS.Timeout;
|
||||
|
||||
skip: (segment: SponsorTime) => void;
|
||||
|
||||
constructor(props: SkipButtonControlBarProps) {
|
||||
this.skip = props.skip;
|
||||
|
||||
this.container = document.createElement("div");
|
||||
this.container.classList.add("skipButtonControlBarContainer");
|
||||
this.container.classList.add("hidden");
|
||||
|
||||
this.skipIcon = document.createElement("img");
|
||||
this.skipIcon.src = chrome.runtime.getURL("icons/skipIcon.svg");
|
||||
this.skipIcon.classList.add("ytp-button");
|
||||
this.skipIcon.id = "sbSkipIconControlBarImage";
|
||||
|
||||
this.textContainer = document.createElement("div");
|
||||
|
||||
this.container.appendChild(this.skipIcon);
|
||||
this.container.appendChild(this.textContainer);
|
||||
this.container.addEventListener("click", () => this.toggleSkip());
|
||||
this.container.addEventListener("mouseenter", () => this.stopTimer());
|
||||
this.container.addEventListener("mouseleave", () => this.startTimer());
|
||||
}
|
||||
|
||||
attachToPage(): void {
|
||||
const leftControlsContainer = document.querySelector(".ytp-left-controls");
|
||||
this.chapterText = document.querySelector(".ytp-chapter-container");
|
||||
|
||||
if (!leftControlsContainer.contains(this.container)) {
|
||||
leftControlsContainer.insertBefore(this.container, this.chapterText);
|
||||
}
|
||||
}
|
||||
|
||||
enable(segment: SponsorTime): void {
|
||||
this.segment = segment;
|
||||
this.refreshText();
|
||||
|
||||
this.startTimer();
|
||||
}
|
||||
|
||||
refreshText(): void {
|
||||
if (this.segment) {
|
||||
this.chapterText?.classList?.add("hidden");
|
||||
this.container.classList.remove("hidden");
|
||||
this.textContainer.innerText = getSkippingText([this.segment], false) + (this.showKeybindHint ? " (" + Config.config.skipKeybind + ")" : "");
|
||||
}
|
||||
}
|
||||
|
||||
setShowKeybindHint(show: boolean): void {
|
||||
this.showKeybindHint = show;
|
||||
|
||||
this.refreshText();
|
||||
}
|
||||
|
||||
stopTimer(): void {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
startTimer(): void {
|
||||
this.stopTimer();
|
||||
this.timeout = setTimeout(() => this.disable(), Config.config.skipNoticeDuration * 1000);
|
||||
}
|
||||
|
||||
disable(): void {
|
||||
this.container.classList.add("hidden");
|
||||
this.chapterText?.classList?.remove("hidden");
|
||||
}
|
||||
|
||||
toggleSkip(): void {
|
||||
this.skip(this.segment);
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ class SkipNotice {
|
|||
|
||||
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
|
||||
|
||||
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer) {
|
||||
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer, unskipTime: number = null) {
|
||||
this.skipNoticeRef = React.createRef();
|
||||
|
||||
this.segments = segments;
|
||||
|
@ -44,11 +44,19 @@ class SkipNotice {
|
|||
autoSkip={autoSkip}
|
||||
contentContainer={contentContainer}
|
||||
ref={this.skipNoticeRef}
|
||||
closeListener={() => this.close()} />,
|
||||
closeListener={() => this.close()}
|
||||
smaller={true}
|
||||
unskipTime={unskipTime} />,
|
||||
this.noticeElement
|
||||
);
|
||||
}
|
||||
|
||||
setShowKeybindHint(value: boolean): void {
|
||||
this.skipNoticeRef.current.setState({
|
||||
showKeybindHint: value
|
||||
});
|
||||
}
|
||||
|
||||
close(): void {
|
||||
ReactDOM.unmountComponentAtNode(this.noticeElement);
|
||||
|
||||
|
|
34
src/types.ts
34
src/types.ts
|
@ -4,9 +4,9 @@ import SkipNotice from "./render/SkipNotice";
|
|||
|
||||
export interface ContentContainer {
|
||||
(): {
|
||||
vote: (type: number, UUID: string, category?: string, skipNotice?: SkipNoticeComponent) => void,
|
||||
vote: (type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent) => void,
|
||||
dontShowNoticeAgain: () => void,
|
||||
unskipSponsorTime: (segment: SponsorTime) => void,
|
||||
unskipSponsorTime: (segment: SponsorTime, unskipTime: number) => void,
|
||||
sponsorTimes: SponsorTime[],
|
||||
sponsorTimesSubmitting: SponsorTime[],
|
||||
skipNotices: SkipNotice[],
|
||||
|
@ -41,7 +41,7 @@ export enum CategorySkipOption {
|
|||
}
|
||||
|
||||
export interface CategorySelection {
|
||||
name: string;
|
||||
name: Category;
|
||||
option: CategorySkipOption
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,14 @@ export enum SponsorHideType {
|
|||
MinimumDuration
|
||||
}
|
||||
|
||||
export enum CategoryActionType {
|
||||
Skippable = "", // Strings are used to find proper language configs
|
||||
POI = "_POI"
|
||||
}
|
||||
|
||||
export type SegmentUUID = string & { __segmentUUIDBrand: unknown };
|
||||
export type Category = string & { __categoryBrand: unknown };
|
||||
|
||||
export enum SponsorSourceType {
|
||||
Server = undefined,
|
||||
Local = 1
|
||||
|
@ -58,9 +66,9 @@ export enum SponsorSourceType {
|
|||
|
||||
export interface SponsorTime {
|
||||
segment: [number] | [number, number];
|
||||
UUID: string;
|
||||
UUID: SegmentUUID;
|
||||
|
||||
category: string;
|
||||
category: Category;
|
||||
|
||||
hidden?: SponsorHideType;
|
||||
source?: SponsorSourceType;
|
||||
|
@ -151,7 +159,7 @@ export interface VideoInfo {
|
|||
isUnlisted: boolean,
|
||||
hasYpcMetadata: boolean,
|
||||
viewCount: string,
|
||||
category: string,
|
||||
category: Category,
|
||||
publishDate: string,
|
||||
ownerChannelName: string,
|
||||
uploadDate: string,
|
||||
|
@ -178,3 +186,17 @@ export interface ChannelIDInfo {
|
|||
id: string,
|
||||
status: ChannelIDStatus
|
||||
}
|
||||
|
||||
export interface SkipToTimeParams {
|
||||
v: HTMLVideoElement,
|
||||
skipTime: number[],
|
||||
skippingSegments: SponsorTime[],
|
||||
openNotice: boolean,
|
||||
forceAutoSkip?: boolean,
|
||||
unskipTime?: number
|
||||
}
|
||||
|
||||
export interface ToggleSkippable {
|
||||
toggleSkip: () => void;
|
||||
setShowKeybindHint: (show: boolean) => void;
|
||||
}
|
|
@ -356,7 +356,7 @@ export default class Utils {
|
|||
}, (response) => {
|
||||
resolve(response);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
23
src/utils/categoryUtils.ts
Normal file
23
src/utils/categoryUtils.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { Category, CategoryActionType, SponsorTime } from "../types";
|
||||
|
||||
export function getSkippingText(segments: SponsorTime[], autoSkip: boolean): string {
|
||||
const categoryName = chrome.i18n.getMessage(segments.length > 1 ? "multipleSegments"
|
||||
: "category_" + segments[0].category + "_short") || chrome.i18n.getMessage("category_" + segments[0].category);
|
||||
if (autoSkip) {
|
||||
const messageId = getCategoryActionType(segments[0].category) === CategoryActionType.Skippable
|
||||
? "skipped" : "skipped_to_category";
|
||||
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
|
||||
} else {
|
||||
const messageId = getCategoryActionType(segments[0].category) === CategoryActionType.Skippable
|
||||
? "skip_category" : "skip_to_category";
|
||||
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
|
||||
}
|
||||
}
|
||||
|
||||
export function getCategoryActionType(category: Category): CategoryActionType {
|
||||
if (category.startsWith("poi_")) {
|
||||
return CategoryActionType.POI;
|
||||
} else {
|
||||
return CategoryActionType.Skippable;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue