Compare commits

...

1291 Commits

Author SHA1 Message Date
78395e3ea0 Minor fixes for comms and mutation detectìon 2026-06-06 03:29:46 +02:00
e269ff3d79 Don't trip on subtitles that don't fall outside current crop area
Todo: don't trip on subtitles that wouldn't get cropped, but that would require Aard to know player's aspect ratio, and it currently doesnt
2026-06-06 03:27:49 +02:00
be495b1c56 daon't error if context isnt defined 2026-04-03 16:44:41 +02:00
3f032619b4 Fix firefox errors in cross-iframe communiccation 2026-04-03 16:44:12 +02:00
ef5ba706b7 Changelog update 2026-01-29 00:00:11 +01:00
3b91c2224c Allow zoom modes to be set as default crop 2026-01-28 23:54:26 +01:00
822d069b82 Finish syncing UI with extension state
Add active mode indicators to popup. Add handling of locked zoom & alignment indicator to both popup and UI. Improve message passing through Comms.
2026-01-28 21:49:51 +01:00
8af9f2cedf Indicate currently selected option in the in-player popup menu 2026-01-28 18:11:57 +01:00
3d8d8eb199 broadcast scaling parameters on ar change 2026-01-28 02:44:38 +01:00
a0d599ce2f add amazon to extconfpatches 2026-01-28 02:05:11 +01:00
fd7b5ec04e Fix messaging 2026-01-28 02:02:27 +01:00
c9818c92b3 Update readme 2026-01-27 20:37:32 +01:00
f57163f2bb Player selection should be functional enough 2026-01-27 20:36:50 +01:00
dbdcb4e367 add option to always activate menu while holding CTRL 2026-01-25 23:33:37 +01:00
e1dd0f9c04 Open in-player settings window, get player selection to somewhat work 2026-01-25 21:47:31 +01:00
2cb6a88c5e fix UI settings a bit 2026-01-25 21:41:01 +01:00
eb89ba03db Fix config 2026-01-25 20:54:25 +01:00
cb02c8619d Ensure each command gets executed once, even if it gets duplicated on the way 2026-01-25 20:52:25 +01:00
7d296e6a3d Get in-page player picker to at least show up 2026-01-23 22:46:59 +01:00
35bb5b477b Finalize menu position settings, replace eventBus.sendToTunnel() with eventBus.send() 2026-01-15 02:33:21 +01:00
925a9fdea3 Other minor settings, add CSS we forgot about 2026-01-14 22:41:46 +01:00
f0baa6ad67 Enable distance-based UI showing. Add "show while holding CTRL" option to the in-player UI 2026-01-14 22:39:42 +01:00
f25749470b be a bit more dilligent about including visitedBusses 2026-01-14 22:38:08 +01:00
8a4985e6a3 Fix problem where aspect ratio wasn't getting set at all 2026-01-13 18:15:48 +01:00
2cce60fb9f Rework message passing and simplify tunnels 2026-01-12 01:07:45 +01:00
826acf9a3f add css for info and warnings 2026-01-12 01:06:23 +01:00
186de15781 fix manual player detection 2026-01-12 01:06:12 +01:00
17dc69f87a Fix player detection when top-layer is being used 2026-01-12 01:04:05 +01:00
8268c7a7af Housekeeping/convert some more imports to absolute paths 2026-01-07 18:04:34 +01:00
a71b99ff10 Mostly import fixes 2026-01-07 09:44:44 +01:00
54c4f49edb Sort out types 2026-01-07 01:42:37 +01:00
92cd1cf58b Fix enum parsing in site settings panel 2026-01-06 16:57:47 +01:00
64570c71ce Fix some default keyboard shortcuts + change some other behaviour about kbd 2026-01-06 01:10:14 +01:00
cbc4b7877f Add cross-iframe messaging (for UI windows) 2026-01-04 01:25:35 +01:00
b62f6feaea Open in-page settings inside floating windows 2026-01-04 00:54:59 +01:00
b6dec385dc Fix bug with zoom not working if AR hasn't been set yet 2026-01-03 23:30:45 +01:00
a12581ed0e Setup interactions for the custom elements of the in-player menu 2026-01-03 19:30:46 +01:00
e651e51c50 Add zoom sliders to in-player popup (non-functional) 2026-01-03 03:19:03 +01:00
56321e52fc Fix menu alignment, fix issue where overlay div sometimes incorrectly inherited bg color, allow custom HTML to be passed as string 2026-01-02 19:20:17 +01:00
73eec1f7ba Fix menu css 2026-01-02 17:47:42 +01:00
a0cddb256a WIP 2025-12-29 14:28:00 +01:00
14b3571af3 Get menu alignment to somewhat work 2025-12-29 11:32:49 +01:00
af08c5094d Fix CSS processing 2025-12-29 02:34:46 +01:00
ef12dc8cc5 Start working on client menu, sort out CSS injection 2025-12-29 00:21:03 +01:00
fcfc0361dc Settings page is now on some baseline of "works", but needs some minor touch-ups 2025-12-28 00:13:06 +01:00
a8847bdeff Save player element settigs + simplified import/export 2025-12-27 23:22:54 +01:00
3f5ab5ecc6 WIP on player detection config import 2025-12-27 03:20:28 +01:00
077c7cae15 Fix migrations of extension enabled status 2025-12-26 22:50:56 +01:00
f916cffd41 Fix migrations 2025-12-26 22:32:36 +01:00
ea4fb95a3c Version bump 2025-12-26 21:40:19 +01:00
3dbec85269 Migrate DOMConfig in preparation for new settings page 2025-12-26 21:39:34 +01:00
2a42c3e715 More or less finish rework of how setting defaults work 2025-12-26 21:36:15 +01:00
c2a6a3f082 Extension enabled mode is now being set correctly 2025-12-22 14:48:50 +01:00
61972a7b2d Fix logger init in popup 2025-12-22 03:53:20 +01:00
4a562443b1 Fix CSS styles not getting applied 2025-12-22 03:36:11 +01:00
9515c1b01a Various fixes, trying to get application of user css styles to work again- 2025-12-22 03:23:10 +01:00
126d891e5e Work around chrome's console.log limitations. 2025-12-22 02:55:33 +01:00
dad1df0244 FIX LOGGER 2025-12-20 18:06:11 +01:00
aff429e5ea Start working on advanced player selector 2025-12-18 00:10:55 +01:00
3457c4fcdc cleanup 2025-12-17 18:35:24 +01:00
74c0358d33 Add snapshot creation and snapshot editing _for real_ 2025-12-17 01:50:50 +01:00
8735d3e81a Removed previous @updated page 2025-12-16 22:58:54 +01:00
4180d5b5b1 Settings snapshot handling, moved update page to unified settings page 2025-12-16 22:57:19 +01:00
ca751a660f Move files around, clean up imports 2025-12-15 19:42:07 +01:00
fb7f5cf1ba More migrating 2025-12-11 02:43:39 +01:00
fe2f52d76a Bump version 2025-12-10 22:50:56 +01:00
ee32ae93c3 Simplify extensionMode things 2025-12-10 22:49:59 +01:00
877fc96da1 Add LegacyExtensionMode enum 2025-12-10 22:11:23 +01:00
67a5b4e05a update nvmrc 2025-12-10 22:11:01 +01:00
d0a5083841 WIP popup redesign 2025-12-10 22:10:40 +01:00
2d8c078a6f Ensure SVG styles don't escape containment 2025-12-03 03:23:31 +01:00
f543dcff24 Start on work on popup, mostly finish work on settings page 2025-12-03 03:15:43 +01:00
5cfa5b0add Redesign of autodetection options 2025-12-02 01:03:54 +01:00
13feb1e822 Merge branch 'dependency-upgrades' 2025-12-02 01:03:05 +01:00
dc755dbe62 Finish migration to tailwind, add new settings screen 2025-12-02 01:02:09 +01:00
b334707463 Convince tailwind to work 2025-12-01 01:14:08 +01:00
ca093f5b34 Upgrade libraries 2025-12-01 01:03:12 +01:00
2e4226b828 cleanup of player detection 2025-11-30 21:27:26 +01:00
095a3daadd Refactor playerData/player size changing a bit. Settings can now only be patched from ExtConfPatches 2025-10-14 20:26:35 +02:00
baa7c79f6c this should be included in "re-add legacy aard" commit 2025-10-14 18:56:48 +02:00
f5e308c5b4 Add update screen 2025-10-14 18:55:45 +02:00
c0c29759fa Re-add legacy AR detect 2025-10-14 18:54:06 +02:00
ddffbe92a0 Finish rewriting Aard 2025-10-13 17:35:37 +02:00
888e48eb5b Refactor aard 2025-10-12 22:03:55 +02:00
61e7351d0b Version bump 2025-10-08 19:40:49 +02:00
4b0571e123 Change how small player windows are checked 2025-10-08 19:40:28 +02:00
bf7f707ab7 change some bits 2025-10-08 19:39:55 +02:00
accc320971 Add options for controlling how aard runs in small players and background tabs + allow auto-disabling aard after first detection 2025-10-08 19:37:25 +02:00
4276a9f03a More logging while building 2025-10-08 16:54:59 +02:00
7ae3fb6109 Fix angle backend detection, though that should no longer be relevant 2025-10-08 15:06:19 +02:00
8c96e2fd90 New iframe inheritance should somewhat work now 2025-08-03 02:58:59 +02:00
30e5bce38f Changelog fix 2025-08-03 02:58:55 +02:00
620028db6d Add validation to aspect ratio input in shortcut settings, fix parsing of aspect ratios given as X:Y 2025-08-03 02:58:02 +02:00
850b5d6b25 Add 'iframes shouldn't inherit' option to site settings 2025-07-16 22:27:05 +02:00
eaf003f5a7 Rework player selector tutorial 2025-07-16 22:26:25 +02:00
ab1efbe9cf Provisional version bump 2025-07-16 03:17:14 +02:00
14f8453067 Update player UI element on fullscreen change; when in fullscreen, limit potential player UI elements to full screen element (if it exists) 2025-07-16 02:53:40 +02:00
090f112463 Get iframe inheritance to work in the popup 2025-07-16 02:49:19 +02:00
01d569f1b5 Update missing types 2025-07-15 19:46:22 +02:00
75670ef3f9 Move settings to a different folder, iframes default to parent page settings 2025-07-15 19:42:35 +02:00
f88f703e84 move settings (update imports only) 2025-07-15 19:41:00 +02:00
a5fdef0196 version bump for release 2025-06-18 02:19:18 +02:00
f10eee335d version bump for testing version 2025-06-18 01:41:02 +02:00
20856891b6 Don't open global settings window in iframe 2025-06-18 01:15:45 +02:00
60dd1193ab Detect embedded content 2025-06-18 01:04:42 +02:00
2bc95af73c Fix iframe detection 2025-06-17 22:49:16 +02:00
178bb65b4a Improve embedded frame detection 2025-06-17 20:12:44 +02:00
f7f046ea53 Propagate messages across server boundary 2025-06-16 21:58:35 +02:00
ec0896e17a Implement zoom in extension popup, in-player UI 2025-05-19 01:18:48 +02:00
a5f35248bd Event-bus: Auto-forward across iframe and commsServer 2025-05-19 01:17:09 +02:00
a05eccce9e fix zoom config 2025-05-19 01:15:05 +02:00
e118777636 Fix debounce 2025-05-19 01:14:19 +02:00
7998e8158e Fix log coloration 2025-05-07 00:45:08 +02:00
14f837d1f0 EventBus: prevent backflow of messages that originate from CommsServer back into CommsServer 2025-05-07 00:42:25 +02:00
cff1d3cf16 Cache active tab in comms server
Previously, if console window was active, this.activeTab would return nothing, and the popup would break. If that happens now, popup will pretend it's on the last active site.
2025-05-07 00:41:45 +02:00
b2cd96982e Further logging fixes 2025-05-04 21:00:15 +02:00
a89eb8e857 Whoops, missed some bits in the cleanup 2025-05-04 02:37:45 +02:00
7c1ccd2cc9 Add contributing.md and license.md 2025-05-04 02:30:56 +02:00
e3a12bc601 Cleanup of unused imports 2025-05-04 02:29:33 +02:00
e20508956a Replace old logger with new logger 2025-05-04 02:18:58 +02:00
4000d0e027 Start rewriting logs 2025-05-01 00:55:22 +02:00
77fcced776 Fix saving settings for sites that inherit configurations from a different site 2025-04-28 01:38:13 +02:00
e67b5a227f Allow some cross-domain settings sharing, handle settings inheritance, don't save auto-detected player index 2025-04-28 01:23:12 +02:00
150a0a8a90 Process iframe data in the popup, remove vestigal code 2025-04-27 19:51:21 +02:00
269dddc92e Fix legacy canvas loading 2025-04-27 17:53:50 +02:00
508ef5cbbb Fix calculation of averages in AardTimers 2025-04-27 17:52:04 +02:00
500d06204c Move default settings to 'other sites' tab 2025-04-27 01:12:06 +02:00
fe8436179f Remove orphan components, update dictionary 2025-04-26 23:28:17 +02:00
adedfec1d0 Update popup appearance 2025-04-26 23:27:09 +02:00
b974111eb4 Fix zoom 2025-04-26 23:24:57 +02:00
3d7b50a2e3 Get zoom options to mostly work 2025-04-26 04:23:57 +02:00
32c11d1f61 Fix snapshots 2025-04-26 01:11:51 +02:00
5c48747d0f Add new zoom options (but they don't do anything yet) 2025-04-26 01:11:31 +02:00
5a0abb3595 Fix settings migrations 2025-04-26 01:09:49 +02:00
95b98c9053 Fix loading snapshots 2025-04-26 01:08:43 +02:00
518e09e3cb Changelog 2025-04-22 02:53:05 +02:00
ad632b0680 Add snapshots to settings page 2025-04-22 02:52:41 +02:00
73aa925067 Don't do AR changes on negative aspect ratios 2025-04-22 02:44:28 +02:00
391b0ac7ab Settings snapshots 2025-04-22 02:43:44 +02:00
ae2bb7afcd Fix UI hiding 2025-04-22 02:37:36 +02:00
0681c3ae08 UI improvements 2025-04-22 02:36:34 +02:00
4aa96bb288 Changes to debugging UI 2025-04-21 22:24:32 +02:00
b24739d70d Put "pls report bugs" in 'what's new' panel as well 2025-04-20 19:50:51 +02:00
dffe8e055a put dev overlays into settings 2025-04-20 19:29:47 +02:00
adadc8bb3e version bump 2025-04-20 19:28:59 +02:00
5f3562fe5b Remove orphan settings 2025-04-20 16:11:57 +02:00
0d7c535a70 revive zoom 2025-04-20 16:11:17 +02:00
39f39f23a7 Prevent more than one concurrent trackDimensionChanges from running 2025-04-15 19:23:42 +02:00
ff0de6f2e7 Version bump 2025-04-15 19:05:48 +02:00
8658b3c0d3 Changelog 2025-04-15 19:03:29 +02:00
8551bc2dc1 Add performance measurements to Aard 2025-04-15 18:51:34 +02:00
a0abf336cf fix #269 2025-04-10 00:46:44 +02:00
7d71dd0507 Allow debug canvas to be shown or hidden from settings 2025-04-10 00:46:22 +02:00
54c190f86c Updated toys 2025-04-05 01:31:06 +02:00
e198c69741 Added toys that enable visual debugging of Aard 2025-04-03 02:59:25 +02:00
7c1d3e52e5 Instead of elements closer to the <video> tag, prefer best-matching element closer to page root.
This ensures UI always renders on top of player if enabled.
2025-03-31 23:26:03 +02:00
5460719297 css fixes 2025-03-31 00:28:35 +02:00
83b770e4e8 Start updating changelog 2025-03-31 00:26:14 +02:00
a5c9835d9d Fix problem where 'default' is always 'disabled' 2025-03-31 00:20:43 +02:00
f0840b78e1 Change handling of getPlayer() 2025-03-30 23:49:13 +02:00
ab79f1e0e6 TODO: some day finish rewriting UI in a way that loads iframe only when expressly requested. 2025-03-30 23:48:24 +02:00
16a2f511bb Replaced v-model in <select> with :value
Did this really fix #270?
2025-03-30 01:27:28 +01:00
39fdd72e9f Don't propagate bad site data from uwserver to extension popup 2025-03-30 01:24:37 +01:00
f93a7e284c Remove InPlayerUIAdvertisement component 2025-03-30 01:24:08 +01:00
b5e480a486 'dev mode' is now saved under settings. 'debug' panel is only shown if dev mode is active 2025-03-30 00:54:47 +01:00
bf128babcc Started logger rewrite. Gave up. 2025-03-30 00:27:50 +01:00
46d1ad221f Re-add debugger panel, strip down json editor 2025-03-30 00:04:30 +01:00
7aacbec108 Reset settings editor on settings import and reset 2025-03-29 17:17:20 +01:00
f77506c454 Settings export, settings import, json editor for settings 2025-03-29 17:11:04 +01:00
e3f1f21ce9 Add basic confirm button. 2025-03-18 18:58:31 +01:00
08c86615a0 Move 'show in-player UI' to popup header 2025-03-14 02:46:59 +01:00
b0425b37c0 minor style tweaks 2025-01-30 02:40:04 +01:00
d94be7aa5e Changelog.MD update 2025-01-30 01:31:11 +01:00
40bdddd031 Remove unnecessary things from the overlay 2025-01-30 01:30:08 +01:00
da94dee780 Make window background darker until chrome doesn't fix backdrop-filter bug 2025-01-30 01:29:48 +01:00
587a21c5f6 Fix default settings 2025-01-30 01:29:00 +01:00
f994617d44 Don't reset settings on failed extconfpatches application 2025-01-30 01:28:44 +01:00
a92f5fc2a1 Fix enabling/disabling extension while switching between environments 2025-01-29 23:58:16 +01:00
208a360c47 Fix settings cascade in ui 2025-01-29 23:56:50 +01:00
f728cbf817 change styles for scrollbars 2025-01-28 00:48:20 +01:00
aef5665cb5 Don't incorrectly set websites as user-defined just because initial player index was set 2025-01-28 00:47:45 +01:00
6ea47d26d0 remove cta from UI advertisement 2025-01-28 00:42:56 +01:00
a7a36033a2 Version bump 2025-01-27 02:59:54 +01:00
32f4d99c98 Move player UI settings and make them site-dependent. Fix SiteSettings always overriding ExtensionStatus.Default with actual default value 2025-01-27 02:59:30 +01:00
2d08a2f339 Hide 'set default stretch' in stretch panel 2025-01-27 02:41:41 +01:00
1f0ebff241 Fix UI not reacting to settings updates 2025-01-26 23:57:02 +01:00
5c26e49c17 disable settings if extension is disabled 2025-01-26 22:10:30 +01:00
ea85f2f5d6 Disable extension on unknown sites by default 2025-01-26 22:10:06 +01:00
af9b123e8d Rework settings popup 2025-01-26 22:09:32 +01:00
d322c0909a Merge branch 'hotfix/iframe-fix' 2025-01-25 21:19:55 +01:00
0bfe84d73d fix saving settings 2025-01-25 21:18:04 +01:00
244e8227e7 Settings: make setProp() functional 2025-01-25 21:17:20 +01:00
5da8073235 fix conflict 2025-01-25 21:15:26 +01:00
5479b7ee95 Fix the iframe problem 2025-01-25 20:51:22 +01:00
b4e932581c fix setting default alignment 2025-01-14 02:41:20 +01:00
1f65cca002 fix support level indicator 2025-01-14 02:41:06 +01:00
1a9cdfa93b additional fixes 2025-01-14 01:51:16 +01:00
d780a8cb12 Fix stretch mode 2025-01-14 01:44:25 +01:00
eaf619f4f8 Revert "Temporarily comment out zoom from player overlay"
This reverts commit ebdc26a00d.
2025-01-13 01:42:58 +01:00
3dd8584954 disable zoom options in player UI for the time being 2025-01-13 01:42:38 +01:00
ebdc26a00d Temporarily comment out zoom from player overlay 2025-01-13 01:33:58 +01:00
dd47dd09da Add about panel back to the popup 2025-01-13 01:31:59 +01:00
bc6d2ac549 Add call to arms to popup. Fix some minor issues 2025-01-13 01:22:49 +01:00
b2cd8197d4 Updated labels of in-player UI 2025-01-12 23:25:39 +01:00
23ea9e77c4 Added zoom UI 2025-01-12 23:25:20 +01:00
42ac51f1e9 CSUI: set opacity to 0 when extension menu is not triggered 2025-01-12 23:24:01 +01:00
d433878680 stop using relative imports in some non-css components 2025-01-12 23:21:51 +01:00
11fa12001c stop using relative imports for most of css 2025-01-10 19:13:27 +01:00
a82a6963e9 version bump 2025-01-10 19:12:40 +01:00
a98a80001c basic iframe for notifications 2025-01-07 08:23:11 +01:00
ad1e73d0cc Try to detect when UI is not clickable 2025-01-06 03:05:18 +01:00
55c0e7e8c5 Get DRM status to UI more reliably 2025-01-05 02:47:45 +01:00
3d35ae32c8 Throw error if upgrading settings fails 2025-01-05 00:38:11 +01:00
33e3bec337 Fix stretch mode while not cropped 2025-01-05 00:37:20 +01:00
9c12272846 attept detecting when wrong player element is detected pt 1 2025-01-05 00:36:41 +01:00
0cc2249b97 version bump but in manifest file 2025-01-05 00:08:09 +01:00
a5d0c0b6b7 fix support level indicator 2025-01-05 00:06:59 +01:00
3736a55bad version bump 2025-01-05 00:06:43 +01:00
303213da73 fix netflix fix some more 2025-01-02 06:32:43 +01:00
8d47eaf74c fix color-scheme issue 2025-01-02 05:50:19 +01:00
9f10c0dcf4 FOR REALS account for offset applied by sites that use top:50%/transformY:-50% to center elements 2025-01-02 04:33:00 +01:00
d981e4c2c6 Account for offset applied by sites that use top:50%/transformY:-50% to center elements 2025-01-01 22:18:37 +01:00
8f3d1bd091 Add illustrations to the player picker 2025-01-01 22:15:22 +01:00
3b9f557075 version bump 2025-01-01 22:14:10 +01:00
18cdec51d9 settings migration 2024-12-31 03:36:08 +01:00
276a51b8c3 WIP on master 2024-12-31 03:15:10 +01:00
14e1d6aad3 even more aard updates 2024-12-31 03:14:29 +01:00
9c65300fc4 more aard updates 2024-12-31 02:50:33 +01:00
cf01dd9397 Try to reduce message-passing to improve performance, make ultrawidify not run on videos smaller than 720p 2024-12-30 23:02:55 +01:00
162318b439 Remove legacy code 2024-12-30 21:39:50 +01:00
cd89cca984 fix vertical alignment 2024-12-30 03:05:47 +01:00
25222c7310 Prevent overlay from blocking the video player UI 2024-12-30 00:55:58 +01:00
b2bfde4426 Fix sliders, but for real this time (pt 2) 2024-12-28 03:41:23 +01:00
8602b53260 Fix sliders, but for real 2024-12-28 03:14:53 +01:00
145b361581 Fix 2024-12-28 02:05:42 +01:00
7743dd8e7f Add sliders 2024-12-28 02:02:01 +01:00
ed2886d449 add minEnabledHeight to settings 2024-12-28 01:42:13 +01:00
002dfacc3e change timeout a bit 2024-12-28 00:42:53 +01:00
fd9fbc32a8 Remove some console.logs 2024-12-28 00:42:40 +01:00
e54deeb4f7 save settings when changing parameters 2024-12-28 00:41:07 +01:00
c85f211d5e Add reset settings to options 2024-12-27 23:13:12 +01:00
67f848a9ee Unbreak showing popup on hover 2024-12-27 23:12:41 +01:00
fe336122e0 Make crop iptions and stretch options panels editable by default without breaking the popup. Add them to UI and keyboard section of settings 2024-12-27 13:18:31 +01:00
6d8d8493a6 Zone editor mostly finished 2024-12-27 04:41:35 +01:00
5d7d249fff Fix overlay positioning 2024-12-27 04:40:30 +01:00
35ca030dfb Close window when opening zone edotir 2024-12-26 17:54:05 +01:00
fab6fb0f5b Fix mousemove position calculation 2024-12-26 17:41:00 +01:00
3b8fc807f7 Add corner markers to editor resizer area 2024-12-26 17:40:27 +01:00
13cfb9ff14 Checkpoint: aard status indicator + trigger zone editor now kinda works 2024-12-26 14:58:14 +01:00
aabd5e75d8 Update changelog 2024-12-22 23:29:43 +01:00
7be6fb9501 Add fallback option class (currently unused) 2024-12-22 23:20:07 +01:00
9fc63e5cd3 Add player UI settings panel 2024-12-22 23:17:32 +01:00
7a1e8be48a attempt to shut some warnings up (but fail) 2024-12-22 23:16:54 +01:00
d299a65e03 fix — open on correct tab 2024-12-22 23:07:55 +01:00
2337f7a3ba add new options to settings interface 2024-12-22 23:06:14 +01:00
4853d7e2fd allow opening of extension settings window on specific tab 2024-12-22 23:05:56 +01:00
ca1a375f2d Limit when in-player UI shows. Add self-expiring menu options that takes you to the proposed, not-yet-implemented UI settings screen 2024-12-17 02:02:49 +01:00
0d098815a2 fix aspect ratio calculations 2024-11-13 01:46:17 +01:00
30cbfb6ffd Make sure that guard line gets invalidated correctly 2024-11-13 01:45:45 +01:00
4c7deffdfe Do full reset of AARD test result every time aard restarts 2024-11-13 01:43:07 +01:00
15133e561f Get autodetection to work at least on a basic level 2024-11-07 02:26:19 +01:00
c235b52e3b fix canvas initialization, add add methods to attach canvas to main body for ez debug 2024-11-07 00:18:40 +01:00
903f356252 Make the context menu slightly less rudimentary 2024-10-24 01:47:46 +02:00
54c44b4379 Add very rudimentary context menu 2024-10-23 01:23:54 +02:00
dcbeecc8cf fix syntax 2024-10-23 01:23:37 +02:00
bf182c65ad Aard should be nominally complete. Probably. 2024-10-22 23:43:25 +02:00
7c5e4101b0 Finish aspect ratio check 2024-10-21 01:08:03 +02:00
2736ac418f WIP on aspect ratio detection 2024-10-19 16:04:20 +02:00
e2dac10501 Finish guardline/image line tests 2024-10-15 17:38:04 +02:00
c15d7ab28e Start re-implementing aspect ratio detection (now with more documentation) 2024-10-14 00:28:13 +02:00
de78f8b1d5 Switch to gl canvas 2024-10-10 00:45:28 +02:00
e56d32180c Add GL canvas wrapper 2024-10-09 23:54:21 +02:00
6c92837317 wip 2024-06-12 20:29:00 +02:00
d4015a86ef Version bump 2024-06-12 09:17:52 +02:00
4b4be96e26 Fix one more link 2024-06-12 09:09:25 +02:00
823e1d983c Remove currently inactive downloads permission 2024-06-12 09:06:12 +02:00
47fba5b81a Fix external links 2024-06-12 03:15:02 +02:00
ee23ebb705 Update permissions for mmanifest v3 2024-06-12 02:17:03 +02:00
d6d3a95daf Last pre-publish fixes 2024-06-12 01:55:12 +02:00
e839e632fc Merge branch 'feature/player-ui' 2024-06-11 02:39:09 +02:00
cb6f0a9a32 Changelog.MD update 2024-06-11 02:36:36 +02:00
c5515934d9 Do the changelog + highlight changelog tab 2024-06-11 02:22:53 +02:00
7dafe85c53 Do the version bump 2024-06-11 01:59:11 +02:00
3bc65c252d I think i disasbled size requirements for in-player UI. Changed wording in popup to reflect that 2024-06-11 01:56:21 +02:00
d4f9ab9f37 Fix player detector panel 2024-06-11 01:55:02 +02:00
b96227b07a plan ahead 2024-06-10 23:40:49 +02:00
da12ee452c Get aard to start reliably if it's set as default aspect ratio for the site 2024-06-10 23:40:33 +02:00
99df043b5f Fix Aard pausing/unpausing when not intended 2024-06-10 23:27:47 +02:00
54d53f1b91 Remove unused code to make Aard work 2024-06-10 23:27:00 +02:00
3645b9ffcf fix typo 2024-06-10 23:26:08 +02:00
9d0ce25be7 Ensure videos get aligned even when aspect ratio is not defined 2024-06-10 00:08:49 +02:00
1c860f74fb remove wiki link, as it's innacurrate 2024-06-09 21:28:57 +02:00
75e31dc3a4 Improve alignment ui 2024-06-09 21:25:11 +02:00
10a8452709 Include changelog 2024-06-09 21:24:26 +02:00
f824f73491 update some libraries 2024-06-09 21:23:25 +02:00
16c34e3d52 ui styling 2024-06-08 03:23:49 +02:00
76c4599dee prepare class names for a day in the future, when we'll move to tailwind 2024-06-08 03:23:34 +02:00
1af80096c7 Add changelog, about panel 2024-06-08 02:10:50 +02:00
cbb2bb4502 Shortcut editor alert 2024-06-06 00:51:47 +02:00
0f3bcaa8e6 Fix alignment/offset handling, make it as player-invariant as possible 2024-06-06 00:51:34 +02:00
ae6575043e fix keyboard shortcut handling for default shortcuts 2024-06-06 00:50:58 +02:00
0dbd5b456c Fix setting keyboard shortcuts 2024-06-06 00:50:08 +02:00
81f3e62446 Pin and close buttons 2024-06-05 23:52:50 +02:00
e0bb25466d UI/global UI mostly works 2024-06-05 01:08:50 +02:00
cb334aab0d Do a bad code, because chrome doesn't work when you try to write good code 2024-06-03 00:16:40 +02:00
9cef850d06 Make extension work on Chrome. Make extension NOT work on firefox. 2024-06-03 00:16:12 +02:00
79ecc87db4 WIP trying to convince in-player UI to show 2024-06-02 16:06:26 +02:00
12644bfbea get manifest v3 to approximately work 2024-05-30 01:02:09 +02:00
bc6b58aa3f Ensure transform offsets are is rounded 2024-05-07 21:02:22 +02:00
abeded1d03 Further migrations to runlevel 2024-05-07 21:02:04 +02:00
a466f77b6e Merge branch 'master' into feature/player-ui 2024-05-07 20:28:39 +02:00
968ce5432a RunLevel works, even though half-assed 2024-05-07 20:24:50 +02:00
277506b3c8 Start implementing run levels 2024-05-07 19:05:10 +02:00
1c48ea1908 Reorder panels in video settings 2024-05-04 01:08:42 +02:00
f2ac1557a4 Fix icon & heading sizes in popup 2024-05-04 01:08:32 +02:00
7213c1e35f Fix padding 2024-05-04 01:08:25 +02:00
9f5f953b46 'Edit shortcuts' bar banner now doesn't take up vertical space while inactive 2024-05-04 01:08:21 +02:00
3ff6054c20 Fix popup (css now finally workS!) 2024-05-04 01:08:14 +02:00
9fb25254a6 Import CSS in vue component instead of html, otherwise popup is without css 2024-05-03 23:13:55 +02:00
8c6a06d490 move fonts 2024-05-03 23:13:07 +02:00
dd37837848 Update node 2024-04-07 15:37:16 +02:00
5ca9325e16 fix 2024-01-28 13:08:31 +01:00
b8fdf0911a Update changelog 2024-01-28 13:01:45 +01:00
a7302fee4b fix angle detection 2024-01-28 13:01:16 +01:00
41bf4d2764 fix 2024-01-28 13:00:48 +01:00
0970140a3a Version bump 2024-01-27 23:29:03 +01:00
b8eb8ec052 rework about panel 2024-01-27 23:06:56 +01:00
46b7d6a25a Round pixel offset 2024-01-27 22:24:09 +01:00
aab2f5b014 fix ard performance display 2023-10-21 17:59:52 +02:00
6062094e6f Fix crashes in chrome 2023-10-21 17:41:09 +02:00
f7e36619df remove manifestv2 2023-10-21 14:04:02 +02:00
167f5173cd get popup to at least mildly work 2023-09-11 00:43:26 +02:00
c7ebd42fe9 some more moving around 2023-09-10 22:09:26 +02:00
94018c6ee3 Cleanup file/directory structure for the UI 2023-09-10 22:06:16 +02:00
388327ec1d fix fs handlers 2023-09-10 19:51:36 +02:00
0ba56c271d hardcode defautl css string somewhere else 2023-09-10 13:12:35 +02:00
bd1216c275 get player select UI to work 2023-07-15 04:17:38 +02:00
3180063fe3 attempt to get player selector to work 2023-07-15 04:03:32 +02:00
9cb9a6607b Remove unused components 2023-07-15 01:29:57 +02:00
42cdcd5c25 Site and extension options: done 2023-07-15 01:28:20 +02:00
8b1da27af7 remove UI when extension is disabled 2023-07-11 00:48:34 +02:00
638e228ce9 don't init UI by default 2023-07-10 22:56:26 +02:00
94469ccd0e mess around with launch.json, still didnt get debugger to work, decided i dont care 2023-07-10 22:05:37 +02:00
48f7e1f39c Merge branch 'feature/manifest-v3-v2' into feature/player-ui 2023-07-10 22:05:02 +02:00
60f7abfa42 get manifest v3 to approximately work 2023-07-10 22:04:30 +02:00
cd391db302 wip 2023-07-10 18:27:06 +02:00
8613d6971f Showing aard notifications is no longer implemented, so we don't call it 2023-04-16 02:54:30 +02:00
cc18153889 Minor update to site settings 2023-04-16 02:53:59 +02:00
be4bf9e2a3 detect whether extension is enabled on dimension changes. Trigger dimension check on fullscreen change 2023-04-16 02:43:50 +02:00
085bd4aab9 fix video alignment default settings 2023-04-16 02:16:57 +02:00
977d103c3a remove aard::halt() (is duplicate of aard::stop() anyway) 2023-04-16 01:55:11 +02:00
0778f07922 sensible variable naming is important + remove redundant code 2023-04-12 23:08:19 +02:00
e12154601b also add 32:9 button 2023-03-30 00:49:09 +02:00
b22d50f760 Firefox also stopped liking the -alpha# format, so version numbering was switched from [next major]-X.X-[alpha|beta|dev]# to [current-major].99.X-# 2023-03-30 00:45:34 +02:00
302f21b477 Cycle mode 2023-03-30 00:43:30 +02:00
65da515854 various fixes 2023-03-29 22:07:50 +02:00
e9bd3154a5 change event bus parameter names 2023-03-29 21:51:06 +02:00
1da147e110 remove redundant comments 2023-03-29 21:49:27 +02:00
e7dd85420c iframe manager 2023-03-02 01:08:25 +01:00
3e542871c6 Attach source frame info when communicating between frames on the same page 2023-03-02 01:08:16 +01:00
26b78f1225 EventBus: Allow forwarding between iframes of a page 2023-03-02 00:37:23 +01:00
57261b5094 Extension settings now holds both extension and site settings 2023-01-23 00:15:56 +01:00
c7f24133e5 fix ANGLE 2023-01-23 00:15:21 +01:00
87aa397a7c Version bump + changelog 2023-01-22 18:04:26 +01:00
e2a18ec765 Extconfpatch for disney+ 2023-01-22 17:58:39 +01:00
26ee2ffbb6 ANGLE popup 2023-01-22 17:45:33 +01:00
38dd1ddbe8 ANGLE detection 2023-01-22 16:06:33 +01:00
11bc79520a ANGLE detection 2023-01-22 16:05:07 +01:00
cc0ae60c83 Update settings object, finalize-ish site settings panel 2023-01-15 22:11:47 +01:00
30b028d836 remove redundant code 2023-01-15 22:10:17 +01:00
a30a70c6b5 get UI to somewhat work. TODO: get scripts to work 2023-01-07 19:19:44 +01:00
9503003a4a migration to site settings mostly done. TODO: check if things still work 2023-01-07 18:57:47 +01:00
a58edad8ea migrate settings to new and simplified SiteSettings 2023-01-07 03:06:37 +01:00
d0ba619b45 Redefine settings interface, write function to migrate some settings on update 2023-01-07 03:05:51 +01:00
64bad6758b start work on new panels 2023-01-07 03:05:03 +01:00
b902b8f9ba fix some warnings 2023-01-06 19:03:44 +01:00
7bb4ee96e7 return zoom slider to its rightful place 2023-01-06 19:03:35 +01:00
6c3e960096 Player is in a separate component 2023-01-06 18:47:42 +01:00
cacb0f1ea0 Put player window into its own component 2023-01-06 18:23:34 +01:00
5a2e33ccd2 fix 2023-01-06 18:23:12 +01:00
5d389a0796 Rename a file for future consistency 2023-01-06 18:22:15 +01:00
6db1d8af2a remove old dist before build 2023-01-06 18:19:59 +01:00
51d73e09ae ?? 🤔🤔🤔🤔 2023-01-06 18:17:08 +01:00
3c324aa1b2 Add global page (not loaded by extension yet), put mousemove probes in a separate file 2023-01-04 18:09:01 +01:00
a94092ac78 remove unnecessary components 2023-01-04 18:08:11 +01:00
f9d72d12b6 minor changes 2023-01-04 18:07:55 +01:00
81e11c2ae9 Extension options WIP 2022-11-22 01:28:04 +01:00
f7a82ccdf7 re-add missing method 2022-11-21 20:46:51 +01:00
44e3395835 Get basic messaging from popup to CS working. TODO: Forwarding to specific iframes/videos 2022-11-21 01:04:57 +01:00
d558717152 Try to recreate some UI in popup 2022-11-20 23:41:40 +01:00
b9b021f466 Spin off VideoSettings component into subcomponents 2022-11-20 22:45:34 +01:00
13bfd63dc2 Move common things between mousehandler and kbhandler to kbmBase 2022-11-20 17:26:57 +01:00
9600c4f1c9 fix kbm whoopsie 2022-11-20 15:23:23 +01:00
de9696471a kbm refactoring stuffs 2022-11-20 14:46:01 +01:00
c954f8b984 add playerMouseHandler 2022-09-28 01:18:58 +02:00
08479242f7 fix settings 2022-09-28 00:40:09 +02:00
d70783cc82 Rename canStartExtension to more descriptive isEnabledForSite 2022-09-28 00:39:20 +02:00
a2f0617d5a start splitting kbmHandler into keyboard and mouse handler 2022-09-28 00:38:36 +02:00
27b6ca824d fix action handler rename 2022-09-27 22:15:01 +02:00
e84f8ee541 start fixing KbmHandler (formerly: action handler) some more 2022-09-27 01:48:08 +02:00
ad87dbdcb9 remove unnecessary logging 2022-09-20 01:38:08 +02:00
9c42c120f5 Remove unnecessary components 2022-09-20 01:35:47 +02:00
740f2e8f6f fix forwarding data between server, content script, and popup 2022-09-20 01:34:59 +02:00
a0388689cf Continue slowly working on unified event bus 2022-08-21 22:46:06 +02:00
46cb5e1078 npm fix 2022-08-21 15:50:03 +02:00
abc53cf00f don't fuck ArDetector up 2022-08-21 15:49:28 +02:00
68f367bd39 Merge branch 'master' into feature/player-ui 2022-08-21 15:45:11 +02:00
d54097799c update cleanup script 2022-08-21 15:24:34 +02:00
ffc25520bb fix types 2022-08-21 15:24:24 +02:00
ccdc68870a Fix #199 2022-08-21 15:04:21 +02:00
7820da181e Readability fixes (I guess) 2022-08-20 16:04:31 +02:00
ec4d43d2ec unpause: Don't resume autodetection if not paused 2022-08-08 23:17:13 +02:00
c12a04b55f mostly whitespace 2022-08-08 23:16:55 +02:00
e0fe4c7103 Version bump + changelog 2022-08-06 01:56:12 +02:00
82f2983ac2 package.lock update 2022-08-06 01:31:40 +02:00
d2733cf486 Fix autodetection getting stuck in 21:9 2022-08-06 01:31:18 +02:00
3aabd298ec fix various fuckies wuckies 2022-07-31 01:12:54 +02:00
a0e375d2dc Cleanup — remove old uwui implementation 2022-07-31 00:18:21 +02:00
8806c8ea0c Integrate comms client/server into eventBus 2022-07-31 00:15:28 +02:00
f0120010fe do types 2022-07-31 00:14:40 +02:00
a5a121ee00 flag for whether autodetection is currently active 2022-07-30 23:07:33 +02:00
459a0eaa91 build lied? 2022-07-30 00:55:20 +02:00
84fefbaa14 remove some console.logs 2022-07-30 00:54:12 +02:00
023b8c370a fix tslint complaints 2022-07-30 00:52:14 +02:00
0a023ac6f2 fix command 2022-07-30 00:47:11 +02:00
83f7e4498a npm audit fix maybe? 2022-07-30 00:32:23 +02:00
d57040fc5b Merge branch 'master' into feature/player-ui 2022-07-30 00:29:23 +02:00
7df62ba530 Version bump + changelog 2022-07-30 00:15:15 +02:00
f51ec50113 fix aard, attempt 3 2022-07-29 00:42:35 +02:00
6748cbe52d update package.lock 2022-07-29 00:25:29 +02:00
fda2dcc484 fix aard: fix harder ? 2022-07-29 00:25:16 +02:00
07a5f06695 Merge branch 'dependabot/npm_and_yarn/shell-quote-1.7.3' 2022-07-29 00:21:52 +02:00
c9392911d9 fix aard? 2022-07-29 00:21:34 +02:00
88dec55116 get DRM status in player ui 2022-07-28 00:45:35 +02:00
f30de3f4a0 Add debug panel with logger 2022-07-27 01:16:14 +02:00
3c5aafee2d update package.lock 2022-07-26 23:55:39 +02:00
52b4e48217 start extension popup rework 2022-07-26 23:54:25 +02:00
58ed3b1ca4 Update changelog 2022-07-26 22:41:12 +02:00
bfef49bc33 fix stuff from merge 2022-07-26 22:40:45 +02:00
b7a61e5bc0 Rework blackframe to not use its own drawImage call 2022-07-26 22:15:39 +02:00
dependabot[bot]
15a3ffd406 Bump shell-quote from 1.7.2 to 1.7.3
Bumps [shell-quote](https://github.com/substack/node-shell-quote) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/substack/node-shell-quote/releases)
- [Changelog](https://github.com/substack/node-shell-quote/blob/master/CHANGELOG.md)
- [Commits](https://github.com/substack/node-shell-quote/compare/v1.7.2...1.7.3)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-26 19:59:10 +00:00
66f934e46e fix version but reals 2022-07-26 21:57:22 +02:00
47b85cfebc Revert "Bump node-sass from 4.14.1 to 7.0.0"
This reverts commit 9a3bd3ff88.
2022-07-26 01:24:33 +02:00
d0b060f31a Changelog + fix version bump 2022-07-26 01:20:29 +02:00
a756a9dc16 Version bump 2022-07-26 01:13:25 +02:00
207b82cc8b Fix set-extension-commnand for people who installed extension while this command was misspelled 2022-07-26 01:12:42 +02:00
26df9633b4 Remove ancient migrations 2022-07-26 01:10:11 +02:00
2b749d6a3b fix set-extension-mode commands 2022-07-26 01:04:36 +02:00
8c403f106c Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/terser-4.8.1' 2022-07-26 00:52:25 +02:00
36aa226e9c Reimplement blackframe. Start timing blackframe execution. Add some placeholder UI for autodetection settings 2022-07-26 00:52:06 +02:00
dependabot[bot]
1946b77285 Bump terser from 4.8.0 to 4.8.1
Bumps [terser](https://github.com/terser/terser) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-20 14:48:52 +00:00
a09ea26b0e Rework blackframe to not use its own drawImage call 2022-07-13 00:22:16 +02:00
1556506bd7 monitor autodetection performance 2022-07-13 00:21:21 +02:00
f94f6ec956 Autodetect script can now report performance data 2022-06-28 22:34:47 +02:00
aae566b82a stuff we missed in previous cleanup 2022-06-28 22:34:20 +02:00
dd8aeefdfd Clean up some performance stuff for autodetection 2022-06-28 20:53:50 +02:00
7d951a5115 comment cleanup 2022-06-28 19:28:14 +02:00
556722693c Rearrange things a bit. No longer do fallback mode 2022-06-28 01:33:21 +02:00
78f350b72b UI design for player selection is mostly done 2022-06-16 00:07:03 +02:00
340ee05443 fix pointer events 2022-06-15 00:53:43 +02:00
561a1aa5a5 Add more styling, choose few quick actions 2022-06-15 00:43:36 +02:00
e2fa4f9cfc some things weren't as "no longer needed" as initially thought 2022-06-14 23:23:08 +02:00
ee56187c9d give player section an icon 2022-06-14 23:20:26 +02:00
d3dbceb749 fix 2022-06-14 23:20:07 +02:00
799c953b30 Remove code no longer needed 2022-06-14 00:27:37 +02:00
e766d6da69 Allow users to set player element from the popup 2022-06-14 00:26:06 +02:00
53ed81fa81 showing and hiding of ultrawidify UI 2022-06-11 15:40:36 +02:00
320d35d9e5 holy shit we did it 🅱️ois, we made some progress on improving manual player detection 2022-06-10 00:22:06 +02:00
a1bfaaaf92 wip 2022-06-09 17:41:04 +02:00
03ebc87aa5 Prepare rendering of basic element tree 2022-06-09 09:01:30 +02:00
d2447d2d8a player detect panel boilerplate 2022-06-09 01:29:26 +02:00
296f146835 Refactor player detection + have player detection log some data for use in settings window 2022-06-09 01:28:46 +02:00
9a7e38d897 dict update 2022-06-09 01:27:50 +02:00
f0fcf996c9 set default stretching and cropping for site and extension in UI 2022-05-25 00:18:54 +02:00
c1b14979a2 also get alignment to work from player UI 2022-05-24 23:48:26 +02:00
9e9fdfe37f get zoom to work again 2022-05-24 23:45:41 +02:00
135ce2dfdd Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/async-2.6.4' 2022-05-06 00:28:52 +02:00
167d46ca76 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/minimist-1.2.6' 2022-05-06 00:28:47 +02:00
23d2c252fe Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/object-path-0.11.8' 2022-05-06 00:28:43 +02:00
9100c1d9de Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/node-fetch-2.6.7' 2022-05-06 00:28:38 +02:00
b45b232ecf Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/path-parse-1.0.7' 2022-05-06 00:28:35 +02:00
9648d30aa2 If videoData is destroyed, remove its eventBus from pageInfo's downstream buses 2022-05-06 00:28:13 +02:00
5a04c2eeee Migrate actionHandler to new commands 2022-05-06 00:23:15 +02:00
74fe85f3a2 Put a parent event bus to pageInfo 2022-05-06 00:23:01 +02:00
4b44efe6fc Finish event bus propagation 2022-05-06 00:22:35 +02:00
dependabot[bot]
8922054c44 Bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-29 00:39:55 +00:00
302353f448 Style dropdowns 2022-04-13 23:03:55 +02:00
3891d946e0 Add new aspect ratios. Fix deleting old aspect ratios. 2022-04-13 21:15:33 +02:00
e92e2e22f5 Update and delete keyboard shortcuts 2022-04-13 01:17:25 +02:00
4f4eb46a60 Clear keyboard shortcut 2022-04-13 00:24:10 +02:00
0daef615c1 Sort out inputs, shortcut button 2022-04-12 00:48:33 +02:00
6ce8bba6b7 Start working on the edit box for crop settings 2022-04-09 15:58:40 +02:00
09cf3ce54e Reorder some methods around into logical sections 2022-04-09 15:37:23 +02:00
872b0dea6e Change 'official' to 'verified' 2022-03-29 02:01:03 +02:00
376f20aeab Show last selected aspect ratio in the UI 2022-03-29 01:53:16 +02:00
57aae96781 Update button css 2022-03-28 23:57:34 +02:00
7c27c33aa0 Prepare some things for edit mode 2022-03-28 23:57:25 +02:00
7dc354509b Remove some of dem pointless console.logs that are no longer needed 2022-03-28 23:56:52 +02:00
7c09440084 UI base: sort out header and tabs 2022-03-28 23:14:52 +02:00
dependabot[bot]
5538ec4d3d Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 16:24:35 +00:00
89168b643c Connect popup (video settings only so far) to content script logic 2022-03-22 01:23:50 +01:00
7fefe255ea Add some things we missed in initial implementation of pointer-events toggling 2022-03-22 01:23:15 +01:00
3423aac49a remove some console.logs 2022-03-22 01:21:18 +01:00
dependabot[bot]
cc8d1d8a94 Bump node-fetch from 2.6.1 to 2.6.7
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.1...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-20 23:52:29 +00:00
dependabot[bot]
c0a863740a Bump object-path from 0.11.5 to 0.11.8
Bumps [object-path](https://github.com/mariocasciaro/object-path) from 0.11.5 to 0.11.8.
- [Release notes](https://github.com/mariocasciaro/object-path/releases)
- [Commits](https://github.com/mariocasciaro/object-path/commits/v0.11.8)

---
updated-dependencies:
- dependency-name: object-path
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-20 23:52:23 +00:00
dependabot[bot]
f006eed113 Bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-20 23:52:22 +00:00
3f131f44d1 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/prismjs-1.27.0' 2022-03-21 00:51:22 +01:00
5cfc089bf1 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/ajv-6.12.6' 2022-03-21 00:51:20 +01:00
987b1ad655 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/node-sass-7.0.0' 2022-03-21 00:51:16 +01:00
1cd06040da Toggle 'pointer-events' based on whether mouse hovers over a csui element or not 2022-03-21 00:50:54 +01:00
f99220bf8b Process messages from content script, respond with results 2022-03-21 00:50:03 +01:00
8bef1d0c99 something's fucky with window.parent.location 2022-03-20 20:56:29 +01:00
47a73cfa6c Get page URL from window.parent.location instead of window.location for iframe 2022-03-20 20:44:50 +01:00
2dd901b1d5 Mark ui as clickable 2022-03-20 20:43:49 +01:00
46725c6fa4 Send mouse coordinates to the iframe 2022-03-20 20:40:11 +01:00
dependabot[bot]
adfc3fca5f Bump prismjs from 1.23.0 to 1.27.0
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.23.0 to 1.27.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.23.0...v1.27.0)

---
updated-dependencies:
- dependency-name: prismjs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-26 02:33:31 +00:00
dependabot[bot]
077610820c Bump ajv from 6.10.2 to 6.12.6
Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.10.2 to 6.12.6.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/v6.10.2...v6.12.6)

---
updated-dependencies:
- dependency-name: ajv
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-12 13:52:45 +00:00
dependabot[bot]
9a3bd3ff88 Bump node-sass from 4.14.1 to 7.0.0
Bumps [node-sass](https://github.com/sass/node-sass) from 4.14.1 to 7.0.0.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.14.1...v7.0.0)

---
updated-dependencies:
- dependency-name: node-sass
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-10 19:16:57 +00:00
85fed9d4c5 Hot dang, icons now show up in Chrome as well.
Also cleaned up some of the mess.
2022-01-29 16:23:40 +01:00
5a2d8d22cb Move extension popup into an iframe, get it to display at least something 2022-01-07 00:50:58 +01:00
761f2c21a8 fixes 2021-11-25 22:16:48 +01:00
c34340d0d1 install mdi font 2021-11-25 22:16:16 +01:00
045cd5a528 Stop using some of the old code 2021-11-25 20:00:32 +01:00
27b0609d34 Let's try to avoid injecting ultrawidify-specific CSS before we actually need it. Part 1/? 2021-11-25 00:31:38 +01:00
65556d9cad We will need to distinguish between two automatic aspect ratio types
AspectRatioType.Automatic: "start autodetection pls"
AspectRatioType.AutomaticUpdate: "Hi I'm aard and I want to change this aspect ratio to something new"
2021-11-25 00:14:06 +01:00
30835e94c0 UI: can set default crop, stretch. (TODO: actually get and use those values) 2021-11-23 01:32:55 +01:00
ee8ac1d9ee Site support level works now ... for most values of work 2021-11-21 02:08:53 +01:00
2be44d54ae Try achieving css class rename some more 2021-11-21 02:08:32 +01:00
cf8bedfad9 fix an oversight 2021-11-20 20:56:49 +01:00
5dc9b498d4 sample jenkins test 2021-11-13 00:57:41 +01:00
fb7a168d31 whitespace 2021-11-13 00:55:17 +01:00
484db037c0 use event bus 2021-11-13 00:54:20 +01:00
15a89583a2 Package updates 2021-11-13 00:48:08 +01:00
75cc0d5a87 handle alignment along y 2021-11-03 00:08:34 +01:00
0e3b962a00 whitespace 2021-11-02 22:20:01 +01:00
afaf65a53d Fix stretch mode from ui 2021-11-02 22:19:50 +01:00
f00d3b5aaf Fix zoom, but for realses 2021-11-02 20:52:01 +01:00
03bd442783 do the version bumping thing again 2021-11-02 20:51:45 +01:00
787bd1a532 Prevent eventBus from crashing everything under certain conditions 2021-11-01 01:18:07 +01:00
bd5befc18c Get sliders to update with values from aspect ratio options 2021-11-01 01:13:13 +01:00
829047585a Get manual zoom to work 2021-11-01 00:11:14 +01:00
78da45d468 cosmetic stuff 2021-10-31 23:18:44 +01:00
7f7ab7b752 Add some things to mitigate windows/wsl suckiness 2021-10-31 19:03:22 +01:00
79fb10c556 Add new format for defining actions. They're called commands now. 2021-10-28 22:53:43 +02:00
ffd9c6fb7a ExtConfPatches to typescrtipt 2021-10-28 22:53:23 +02:00
4074774d3a Preemtive version bump and patch notes 2021-10-27 20:46:14 +02:00
e7d43c566f Fix aspect ratio not getting applied correctly 2021-10-27 20:10:55 +02:00
52b94003bf Fix popup not opening 2021-10-27 20:10:21 +02:00
13bd280062 Get event bus to work (for some values of work) 2021-10-26 23:16:11 +02:00
209e4221d2 Start adding event bus 2021-10-26 22:20:50 +02:00
c4832a9ef7 Comment out temporarily unused commands 2021-10-26 20:11:26 +02:00
6cb8cc174b Prevent multiple concurrent vue app instances 2021-10-26 20:11:03 +02:00
e9b882376c Simplify player data dimension change tracking a bit 2021-10-26 00:30:38 +02:00
a4948d3eef Whitespace 2021-10-25 23:13:35 +02:00
746a78577e UI for manual zoom and video alignment 2021-10-24 00:57:57 +02:00
268ef7dae3 push the popup thing a bit further 2021-10-22 00:31:04 +02:00
6c1d666fdf switch bootstrap icons with mdijs 2021-10-22 00:30:36 +02:00
53fb3e8f46 Add stretch actions to the popup, styling 2021-10-19 22:49:56 +02:00
2df0b1a0fb formatting 2021-10-19 22:49:28 +02:00
3846d1a926 Cropping now also works from in-player UI 2021-10-19 22:49:13 +02:00
2a747e11af Merge branch 'master' into feature/player-ui 2021-10-19 20:27:41 +02:00
cda6b4e10d Version bump n shit 2021-09-19 21:32:47 +02:00
502ce707e1 Style 2021-09-19 21:22:12 +02:00
5b1f73a69e Fix problem with autodetection 2021-09-19 21:22:05 +02:00
f7eba0974f Fix default settings 2021-09-19 21:21:23 +02:00
2ae84bccd8 update changelog 2021-09-15 03:13:02 +02:00
3ac3f1aee5 It would be good to disable frame check if AARD is halted or exited 2021-09-15 01:45:51 +02:00
23ca74afdb Add quick readme to the logger 2021-09-15 00:54:46 +02:00
7d894fb1e3 it is higly recommended to actually resolve those promises 2021-09-15 00:54:23 +02:00
b39e6be952 Add a directory for debugging resources (e.g. logs submitted by users).
Since logs can be rather extensive and, more importantly, may accidentally contain PI, we also have them on gitignore.
2021-09-14 23:25:57 +02:00
415ebf6821 Simplify video rescans 2021-09-14 23:22:59 +02:00
b1ec4f7387 Make framecheck ever so slightly async 2021-09-14 19:12:54 +02:00
820af9b133 Patch for netflix 2021-09-13 22:47:27 +02:00
54c88dcb2f we no longer have gitlab 2021-08-28 02:24:23 +02:00
104ed85d72 Get UI to show again 2021-08-26 01:34:59 +02:00
84de5927cd Try initializing UI again 2021-08-26 01:07:39 +02:00
1b829e095b Merge branch 'master' into feature/player-ui 2021-08-26 00:54:07 +02:00
cbb4624e25 Version bump stuff 2021-08-26 00:45:22 +02:00
2cb02ff3f2 fix 2021-08-26 00:45:12 +02:00
b8cd441109 misc fixes 2021-08-26 00:37:56 +02:00
86a8c23999 Change how halting AARD is handled 2021-08-25 23:13:02 +02:00
a2573b5aaf Add performance timing to ar detector + make it use animationFrame 2021-08-25 22:32:39 +02:00
ea8671f64b Tidy up ArDetector 2021-08-25 20:39:27 +02:00
a234903b8f Fix logging popup a bit 2021-08-24 02:13:05 +02:00
981da95cde The logger should be working again? 2021-08-24 00:57:29 +02:00
18fe17f00f Youtube: fix alignment issues with square-ish videos in normal player (#162). Fix issue with misalignment of featured videos on channel page 2021-08-17 19:39:00 +02:00
5ad8302084 Add missing conf for piped.kavin.rocks (fix #151) 2021-07-14 22:45:00 +02:00
cfcd0c5945 version bump 2021-07-14 22:06:50 +02:00
eb1b1cf73d changelog 2021-07-14 22:06:42 +02:00
95d3d962f4 I reckon we can drop ExtConfPatches for versions over a year old
like, everyone should have receiving this and future updates should be on 5.x, or at least on 4.5.x
2021-07-14 21:19:39 +02:00
6eb96f917d Goodies for #160 2021-07-14 21:17:32 +02:00
5847215097 Merge branch 'master' into feature/player-ui 2021-07-05 01:22:30 +02:00
d102a8cd7d fix popup 2021-07-05 01:17:10 +02:00
571ac92cd3 Version bump & changelog 2021-07-05 01:02:21 +02:00
246743203f whitespace + plex rule 2021-07-05 00:51:15 +02:00
1cc76ab3b1 Improve UI in advanced settings 2021-07-05 00:49:35 +02:00
5c9112a07b package.json: add npm run dev-[browser] scripts 2021-07-05 00:48:59 +02:00
d92b24a2f6 Merge branch 'master' into feature/player-ui 2021-06-13 02:19:39 +02:00
7729bf545e shut up some of dem vue warnings 2021-06-13 02:19:05 +02:00
c14aa99e5b Fix extconfpatches 2021-06-13 02:18:47 +02:00
2ced591ed4 Fix build errors 2021-06-12 23:23:47 +02:00
56dcd0feb3 Merge branch 'master' into feature/player-ui 2021-06-12 23:22:54 +02:00
c43b1daa42 Edge probably doesn't need the 'pls ensure you're up to date' warning anymore 2021-06-12 23:21:01 +02:00
5e0f7c6160 update package.json — npm start now builds for _all_ browsers 2021-06-12 23:12:04 +02:00
cbaa424fc4 Version bumps and changelogs 2021-06-07 22:16:03 +02:00
c2039da7c8 Make some labels less confusing 2021-06-07 21:57:40 +02:00
6d4b161b06 Fix for disneyplus maybe? 2021-06-07 21:56:57 +02:00
fef2c26cbe raise RAM, second attempt 2021-06-07 20:34:27 +02:00
50719113b1 Raise memory limits in build scripts. Also build edge version on the CI 2021-06-07 20:09:35 +02:00
4e407e2cf8 Edge is supported for a while now, actually. 2021-06-07 19:55:05 +02:00
336879a4a1 Version bump & changelog 2021-05-12 00:19:21 +02:00
7fd28616aa Fix incorrect stretch with fix-source stretch 2021-05-12 00:01:40 +02:00
fba6a8d604 Additional details in the popup 2021-05-11 22:45:08 +02:00
e5c1bcded1 Safety check 2021-05-11 22:44:24 +02:00
b99929063c Restore aspect ratio if offsets fail verification 2021-05-11 22:43:27 +02:00
18d7414b90 refactor 2021-05-11 22:42:51 +02:00
Tamius Han
4427844a41 Merge pull request #154 from tamius-han/dependabot/npm_and_yarn/lodash-4.17.21
Bump lodash from 4.17.20 to 4.17.21
2021-05-11 14:49:50 +02:00
dependabot[bot]
e92f53f590 Bump lodash from 4.17.20 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-10 14:38:04 +00:00
965a48c086 Remove some code that was only needed cos old edge 2021-04-12 20:54:42 +02:00
0edd82479e Put in console.errors on extension initialization. 2021-04-12 20:54:26 +02:00
338b219142 Since webExt polyfill ensures 'browser' is also present in Chrome, code for opening settings page from popup stopped working in Chrome 2021-04-12 20:36:42 +02:00
c504d5594c Version bump 2021-04-12 20:31:50 +02:00
9beeffc679 Force old chrome installs to force-enable the zoom limit 2021-04-12 20:30:42 +02:00
19e021ce8e Update 'what's new'? 2021-04-12 20:27:49 +02:00
c863447f3d ; 2021-04-12 20:25:44 +02:00
520b0b7ade Extra comments 2021-04-12 19:08:12 +02:00
48e12c8504 Fix player detection on videos wider than the monitor 2021-04-12 19:03:18 +02:00
60f940f4db try/catch in aspect ratio getter 2021-04-12 19:01:28 +02:00
0bed840b6f fix settings interface 2021-04-10 04:17:07 +02:00
ddbddc45a9 Whitespace and stuff 2021-04-10 04:10:22 +02:00
a039fd4ce4 Make isFullscreen a bit more lenient.
innerWidth|Height and screen.width|height no longer need an exact match
2021-04-10 04:10:12 +02:00
d3c3c99069 fix 'is extension enabled' query 2021-04-10 04:09:00 +02:00
843a405323 get settings for current site function 2021-04-10 04:08:35 +02:00
cb548c3a3d aspect ratio is a getter now. Use said getter 2021-04-10 04:08:09 +02:00
d406438527 Enable zoom limit for new installs in Chrome as well 2021-04-10 04:03:14 +02:00
6737061713 Add new settings option 2021-04-10 04:02:52 +02:00
46c635b11b Chrome still has that bug 2021-04-08 21:53:49 +02:00
5f04c03f80 Correct changelog to account for Edge. 2021-04-05 03:31:07 +02:00
dbf96f97b6 Fix :disabled for a certain checkbox 2021-04-05 03:30:49 +02:00
861e62dc56 Add confPatch for Edge 2021-04-05 03:30:29 +02:00
1b00420388 correct the interface 2021-04-05 03:29:56 +02:00
0ac57ca2ac add target:blank to links 2021-04-05 03:29:35 +02:00
58e29f4590 Remove logging, typos 2021-04-04 23:55:49 +02:00
8e544a6ae0 make build-all script windows-friendly (or windows-friendlier)
still requires bash tho
2021-04-04 23:55:34 +02:00
2cf42cb6ed Add link to blog 2021-04-04 23:19:50 +02:00
93eb67ca49 Prevent recursing 2021-04-04 23:19:43 +02:00
58302ea87c move @types/node to dev dependencies 2021-04-04 15:54:28 +02:00
f17bae8f39 more vscode configuration stuff 2021-04-04 15:53:06 +02:00
fee73519e6 formatting 2021-04-04 15:48:46 +02:00
5d1ad6f935 remove unnecessary logging 2021-04-04 15:48:33 +02:00
6d07fc24e4 we did a npm -i again 2021-04-04 15:48:11 +02:00
a720556ac9 typos and dict update 2021-04-04 15:48:01 +02:00
0bc1254ce0 Merge branch 'master' into feature/player-ui 2021-04-04 03:59:08 +02:00
f8bd8e2eec Update "what's new" stuff. 2021-04-04 03:58:26 +02:00
d0beb137fe Version bump 2021-04-04 03:58:08 +02:00
c6e4d06fa1 Allow toggling chrome hardware acceleration bug workaround for people not experiencing it 2021-04-04 03:42:36 +02:00
190737b915 Merge branch 'master' into feature/player-ui 2021-04-01 23:50:08 +02:00
902f880a68 no stray logs 2021-04-01 21:44:07 +02:00
624c072bb3 Changelog + version bump 2021-04-01 21:43:19 +02:00
09253fe0a1 Unbork settings page even more 2021-04-01 21:40:58 +02:00
a0eeabbd50 Fix for #142, possibly 2021-04-01 21:24:19 +02:00
873a8cf3a8 Fix enum imports 2021-04-01 21:23:59 +02:00
9c1fb77dc8 Turn all logging off for release 2021-03-31 00:10:41 +02:00
0a3627c7fe Don't attempt height:auto compensation if style height compensation is active 2021-03-31 00:10:18 +02:00
2a4a3f7192 change mailto link 2021-03-30 23:40:06 +02:00
0b30688d59 update changelog a bit more 2021-03-30 23:39:41 +02:00
5cbfb70306 add some folds to code 2021-03-30 21:55:17 +02:00
63e40cf71a Compensate for height:auto in certain scenarios 2021-03-30 21:54:44 +02:00
d1baebcceb Add types, return crop strategy with stretch factors 2021-03-30 21:54:08 +02:00
c07916362e log a lil bit less. Fix mutation observer callback 2021-03-30 20:38:16 +02:00
d382b46464 Log a lil bit more 2021-03-30 01:11:39 +02:00
78c66a1543 update packages ... some of them, at least, so we can really use ResizeObserver 2021-03-30 01:11:11 +02:00
48319acc31 okay, we can't ditch mutationobserver 5ever 2021-03-30 01:10:37 +02:00
b634e4d2b3 remove some logging 2021-03-29 23:40:34 +02:00
03f37fa1a3 long live ResizeObserver, let's ditch MutationObserver five-ever 2021-03-29 23:30:54 +02:00
3185e500d1 Add resizer observer 2021-03-29 23:29:30 +02:00
bf010a243c changelog 2021-03-29 22:52:32 +02:00
9d2b480f3f Use webextension-polyfill in popup 2021-03-29 21:15:01 +02:00
b69a03d42f code cleanup 2021-03-29 21:14:03 +02:00
f1330570d6 fix popup not working 2021-03-29 21:07:54 +02:00
589a1fefbf fix import 2021-03-14 02:34:35 +01:00
d64b41a363 Revert "split popup into ts"
This reverts commit 73eb5ffe93.
2021-03-14 02:33:04 +01:00
e7613aa691 Revert "okay what the actual fuck"
This reverts commit a9ab8d8b92.
2021-03-14 02:32:40 +01:00
e272d1d4d4 Revert "Move popup to vue"
This reverts commit 5b0fcef15c.
2021-03-14 02:32:34 +01:00
0dc196127e Revert "fix imports"
This reverts commit d644395b35.
2021-03-14 02:32:28 +01:00
42394a48bc Revert "fix vue imports"
This reverts commit cd7a1de548.
2021-03-14 02:32:22 +01:00
6546ad2166 Add module resolution to tsconfig 2021-03-14 02:32:14 +01:00
cd7a1de548 fix vue imports 2021-03-14 02:10:41 +01:00
d644395b35 fix imports 2021-03-14 01:29:05 +01:00
5b0fcef15c Move popup to vue 2021-03-14 00:34:47 +01:00
dependabot[bot]
bd1a904fd5 Bump elliptic from 6.5.3 to 6.5.4
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 23:16:21 +00:00
a9ab8d8b92 okay what the actual fuck 2021-03-12 00:14:52 +01:00
bb3fcc646e Update changelog.md 2021-03-11 22:00:23 +01:00
0f5111e1e2 add vue cli typescript plugin 2021-03-11 21:58:43 +01:00
73eb5ffe93 split popup into ts 2021-03-11 21:58:12 +01:00
8cefa4af2f add vue shim 2021-03-11 21:57:44 +01:00
ede570865f Migrate some more things to typescript 2021-03-11 21:04:09 +01:00
038191d38e Merge branch 'master' into feature/player-ui 2021-03-06 04:07:03 +01:00
6a1f07d881 Merge branch 'typescriptify' 2021-03-06 04:02:59 +01:00
6c59c009e5 Move some last remaining enums to typescript 2021-03-06 04:01:47 +01:00
4354393f79 Do the version bump 2021-03-06 03:57:10 +01:00
d215a10486 move most of the main content/background script to ts 2021-03-06 03:35:33 +01:00
a699166368 Some arguments need to be optional 2021-03-06 03:34:38 +01:00
b83be3de7d More types for logger 2021-03-06 03:34:22 +01:00
db287adaed Remove vestigial code 2021-03-06 02:54:22 +01:00
7714adb843 Fix keyboard shortcuts 2021-03-06 02:53:58 +01:00
3eca178df5 Add type to ports 2021-03-06 02:22:56 +01:00
87310f95cc remove some logs 2021-03-06 02:07:09 +01:00
57ad81a7a4 Move comms to ts (3/3 | client) 2021-03-06 02:06:46 +01:00
c1e0e7b280 Move comms to ts (2/3 | server) 2021-03-06 01:27:28 +01:00
006c9ef975 Move comms to ts (1/3) 2021-03-06 01:23:58 +01:00
08fceafc17 Use browser-polyfill in settings 2021-03-06 01:22:09 +01:00
357d4829eb Fix logger 2021-03-06 01:21:31 +01:00
e5d1addc00 ??? 2021-03-05 23:39:12 +01:00
d4868020f4 DrmDetector can into typescript 2021-03-05 23:35:32 +01:00
7e41768aba Migrate actionhandler to ts 2021-02-20 00:09:17 +01:00
075168ed85 Convert all remaining vars to lets 2021-02-18 22:38:32 +01:00
8475340999 Fix problems in ArDetector and related functions 2021-02-18 22:35:58 +01:00
a3bd8a8b7e convert ArDetector to ts 2021-02-18 22:29:23 +01:00
7e49b493bb Merge branch 'master' into typescriptify 2021-02-18 19:06:37 +01:00
888ffcaded Version bump & update notes 2021-02-18 19:06:23 +01:00
83b8ce0aaa Fix drm detection, but hopefully for real 2021-02-18 19:03:48 +01:00
7f77847baa migrate additional things over from master branch 2021-02-18 01:08:12 +01:00
72da7eb5f4 Merge branch 'master' into typescriptify 2021-02-18 00:15:38 +01:00
5c6ea49c66 Version bump + version notes 2021-02-18 00:13:51 +01:00
6de5ae6355 Fix false positive 'this site cant work cos drm' errors ... xcept for real 2021-02-18 00:13:34 +01:00
ec2cd3d6a0 Fix DRM detector bugs 2021-02-17 01:06:11 +01:00
6031e35133 Add new DRM detection. Do it _before_ launching AARD 2021-02-17 00:51:56 +01:00
b322ef0db1 Fix for #134 2021-02-17 00:23:55 +01:00
4febd2f602 fix 2021-02-17 00:20:30 +01:00
46360d1b1f remove typo from comment 2021-02-09 00:54:04 +01:00
2d8bf0c0ee Move some more files to typescript 2021-02-09 00:52:58 +01:00
a907d0d404 Finish moving videoTransform stuff to typescript 2021-02-09 00:42:52 +01:00
0117d44422 Move settings to typescript. Add interface for settings object 2021-02-09 00:37:54 +01:00
4fdd6af291 Add additional types 2021-02-08 23:23:15 +01:00
9a864bc11c Migrate pseudo-enums to typescript enums. Fix related imports & stuff 2021-02-08 23:04:54 +01:00
ee3ccef0e4 Migrate first batch to typescript 2021-02-08 22:45:51 +01:00
d9053e26b5 update configs 2021-02-08 22:45:41 +01:00
efefa4ef32 update packages 2021-02-08 22:45:34 +01:00
cfc7f6aca6 add new packages 2021-02-08 21:05:27 +01:00
3b79829b72 Do tsconfig and webpack 2021-02-08 20:55:17 +01:00
4d1da636e3 Log just a little on master as well 2021-02-08 20:47:06 +01:00
226cfb654e We don't need this logging on master
This reverts commit dc71a25863.
2021-02-08 20:45:57 +01:00
dc71a25863 Do additional logging 2021-02-08 20:44:49 +01:00
cb5218e52d Only allow one instance of offset validation watcher per video element 2021-02-08 20:44:34 +01:00
f63fd895fe Rework observers a little. Debounce offset validation in videoData 2021-02-08 20:43:56 +01:00
5efa8c88c1 Additional anti-recursing measures, pt 2 2021-02-07 12:54:20 +01:00
870384f83b Decrease callback frequency 2021-02-01 00:11:24 +01:00
02b4b44780 Additional anti-recursing measures 2021-01-31 23:55:50 +01:00
ea186d0aeb always return videoDimensions 2021-01-31 23:32:32 +01:00
96a5b8da8f Add a bit of 'maybe' to "whoops no Aard on this site" notification 2021-01-31 17:57:35 +01:00
f961c8aea8 Don't do logging by default 2021-01-31 17:44:33 +01:00
f3a9c06f92 Anti-recursing measures 2021-01-31 17:44:24 +01:00
78c04523c0 Prevent some more errors 2021-01-31 16:46:33 +01:00
fff575f762 Version bump 2021-01-31 01:54:33 +01:00
bc19164c25 fix pt 4 2021-01-31 01:42:08 +01:00
3ab619799a fix pt 3 2021-01-31 01:34:43 +01:00
f1613b950c fix a lil bit more 2021-01-31 01:18:35 +01:00
ee9599d040 bind this for shits and giggles on onPlayerDimensionsChanged 2021-01-31 01:04:31 +01:00
851f0e0e84 fix pt 2 2021-01-31 00:58:43 +01:00
56ebd2d36c Fix 2021-01-31 00:47:32 +01:00
38d1be5fc2 fix condition 2021-01-31 00:24:02 +01:00
35208cf9e8 log wtf is going on with scaler and resizer 2021-01-30 13:50:11 +01:00
0aa7beb525 Fix chrome condition test 2021-01-30 13:31:30 +01:00
71cb357424 Fix imports 2021-01-30 13:07:19 +01:00
bc5d5046df Webpack fix 2021-01-30 12:21:04 +01:00
5bd2952437 Fix chrome bug .. hopefully 2021-01-30 12:16:37 +01:00
9a0c0ba810 whitespace 2021-01-30 12:16:27 +01:00
a5bf3ec1e0 Remove redundant code 2021-01-30 10:38:45 +01:00
84da78e2e8 expose videoData objects via window.ultrawidify 2021-01-27 00:41:42 +01:00
13b89eb2a9 Add sample button from old config 2021-01-27 00:41:13 +01:00
89c524a690 add video settings placeholder to the UI 2021-01-21 00:21:15 +01:00
d44d1b6e64 add getRuntime() to BrowserDetect 2021-01-21 00:20:43 +01:00
8ea5b3d5be add toString() method to an enum 2021-01-21 00:20:27 +01:00
f8c774700e Merge branch 'master' into feature/player-ui 2021-01-20 22:58:40 +01:00
ef410d9e01 Changelog 2021-01-13 19:33:36 +01:00
328eba1d6c Version bump 2021-01-13 19:33:31 +01:00
b819fc816b Fix DRM detection for Chrome 2021-01-13 01:12:03 +01:00
1410f699c5 Add video status checks 2021-01-13 01:11:55 +01:00
be6df178f6 Optimize catching DRM protected videos. 2021-01-13 01:11:44 +01:00
2a7001ea3a fix 2021-01-13 00:22:42 +01:00
e2eaa206e4 remove console.logs 2021-01-12 23:35:58 +01:00
437890a609 Documentation & comments 2021-01-12 23:28:27 +01:00
2025547d33 Un-initalize videoData on illegal video dimensions ($
When youtube changes between two videos, video dimensions briefly become 0x0. This is super duper illegal and sometimes causes the behaviour described in issue #125. Setting VideoData.videoLoaded to false fixes this.
2021-01-12 23:28:17 +01:00
884db1c5e3 Do event listeners properly 2021-01-12 23:24:20 +01:00
aa1ccedad5 Cosmetic changes 2021-01-12 23:19:04 +01:00
df2b0f5614 Add toString method to aspect ratio enum 2021-01-12 23:18:14 +01:00
aec87e7681 Don't assume jenkins build number is always there, cos it's not on local builds 2021-01-10 20:46:41 +01:00
bf26961aaa Fix formatting in what's new panel 2021-01-10 18:07:58 +01:00
67ad0c54b4 Apply some styling to the edge warning box 2021-01-10 17:43:11 +01:00
fda7b38fa1 fix 2021-01-10 17:25:36 +01:00
5b5f4204bb Version bump 2021-01-10 17:22:12 +01:00
167b9d4305 Disney+ should be fixed? Maybe? See #84 2021-01-10 17:22:02 +01:00
0760e447db Changelog updates 2021-01-10 17:21:23 +01:00
897e65567b add note in the popup 2021-01-10 16:12:35 +01:00
7e403e3406 Edge issues are fixed, so the popup goes. 2021-01-10 16:12:20 +01:00
7cb378029c Some cosmetic changes in BrowserDetect 2020-12-30 15:35:54 +01:00
f4ff7e7ed6 Distinction between chrome build and edge build, and "doesn't matter which of the two" build. 2020-12-30 15:35:08 +01:00
4c359675b6 Build edge version of the addon separately from Chrome 2020-12-30 15:14:03 +01:00
5987fda0b2 Merge branch 'master' into feature/player-ui 2020-12-29 20:36:06 +01:00
066b74e1ac Version bump 2020-12-29 20:34:59 +01:00
af7833a372 Changelog notes 2020-12-29 20:34:02 +01:00
f554030f3c Don't set AR if video dimensions are undefined. 2020-12-29 20:20:00 +01:00
0e0eb57fd9 Merge branch 'master' into feature/player-ui 2020-12-29 18:33:54 +01:00
485a5c13f0 Rewrite popup 2020-12-25 01:27:05 +01:00
bb8d63f67d update base css 2020-12-25 01:26:52 +01:00
6340541daf Attempt to load fonts in popup 2020-12-25 01:26:46 +01:00
028aceef2c Version bump + changelog 2020-12-24 23:27:19 +01:00
5e96cdcad8 Alternative fix for streamable (#128) 2020-12-24 23:26:22 +01:00
c04780bb16 Make a comment explaining reversal to ensure we wont do that thing again 2020-12-24 22:58:48 +01:00
84d33a9ccb Revert "Add 'position:absolute' to video css when setting AR (fix #116)"
This reverts commit a5ef24a50f.
2020-12-24 22:54:20 +01:00
b30369dfea Changelog update 2020-12-23 02:09:30 +01:00
a5ef24a50f Add 'position:absolute' to video css when setting AR (fix #116) 2020-12-23 02:04:06 +01:00
9b1067afc4 Formatting 2020-12-23 02:03:39 +01:00
433057bb19 Edge popup fixes 2020-12-23 02:03:32 +01:00
3704ea7962 Make logger and options object optional for Settings 2020-12-23 02:01:52 +01:00
76b380d6c3 console.log removal 2020-12-23 01:59:49 +01:00
804509b3c5 Move the code that shows the Edge popup back to PlayerData 2020-12-23 01:15:11 +01:00
1e8fd6aad4 Enable aard on netflix for new users, even on Chromium-based browsers
This was done because the edge popup is dependant on autodetection at least attempting to start. Also, we want to show 'autodetection won't work cos drm' on netflix for other chromium-based users.
2020-12-23 01:14:29 +01:00
fbcf4c0c19 Target:blankify all links on the popup. Other minor adjustments. 2020-12-23 01:13:05 +01:00
0234ea7871 Update changelog & bump version 2020-12-22 23:23:36 +01:00
75ec1969c0 Move edge warning popup into the starting phase of autodetection 2020-12-22 23:23:03 +01:00
bcc3d89342 Reset aspect ratio on aard fail 2020-12-22 22:25:41 +01:00
e4194e029c Additional popup updates 2020-12-22 22:25:12 +01:00
d673ed089f Don't commit extension configs submitted by the people reporting a problem. 2020-12-22 21:44:36 +01:00
96ddc8a84b Write the promised blog post & include it in the popup message. 2020-12-22 21:43:45 +01:00
70f7b88d71 Try to fix extension unsetting Netflix' default AR 2020-12-22 03:32:56 +01:00
f174ddab68 Only show the popup in Edge 2020-12-22 03:21:14 +01:00
f7c373481d Add popup for Edge 2020-12-22 03:14:03 +01:00
33d16221cc Remove some logs 2020-12-22 00:12:52 +01:00
dd4d71a496 Add anchor for player UI (base for reworking stuff into Edge 'doesnt work' warning) 2020-12-22 00:12:28 +01:00
c2f1c6980f Fix popup interface not updating — #127 2020-12-21 23:28:23 +01:00
c0c454f3ef Add afterSettingsSaved callback 2020-12-21 23:27:45 +01:00
4b5c5a8dce Fix popup not working at all in Chrome 2020-12-21 19:29:52 +01:00
becf7c328e Add workaround for Chrome refusing to do chrome.runtime from Proxy objects 2020-12-21 19:29:39 +01:00
fbe58ec54d Add static class properties thing to babel 2020-12-21 19:27:34 +01:00
609247cbf0 Fix 'fit height' a little 2020-12-20 01:00:06 +01:00
a9f99d93be Fix aspect ratio calculations on height-compensated videos, episode 1 2020-12-19 03:18:14 +01:00
be82c4dae6 Fix a crash 2020-12-19 03:02:20 +01:00
94a9b1e56f fix 2020-12-18 01:50:00 +01:00
769ff6aace Fix the issue where videos on players that were narrower than the video got cropped a lil bit too much 2020-12-18 01:44:45 +01:00
6e1fe930ac Remove Edge/Microsoft store links from description 2020-12-17 01:51:10 +01:00
93b8049b01 Move debug data into a separate component/panel 2020-12-17 01:49:28 +01:00
5cf74b4405 Fix typo in one of the mutations 2020-12-17 01:49:15 +01:00
2aa91df820 Add additional data to debug popup 2020-12-17 01:48:45 +01:00
85fedbfeaf Fix base css 2020-12-17 01:48:29 +01:00
1cbe74252c display some data 2020-12-16 01:40:09 +01:00
9fb9667dba fix base css 2020-12-16 01:36:00 +01:00
55b6ca7392 fix comms 2020-12-16 01:35:39 +01:00
758c2bf0bc Report data to the UI 2020-12-16 00:19:37 +01:00
6eed271814 Display such data in a very rudamentary way 2020-12-16 00:19:20 +01:00
7a5a4bddf8 support passing debug data between content script and UI 2020-12-16 00:19:02 +01:00
3f8ea683ef Add toString() method to selected enums 2020-12-16 00:18:23 +01:00
29dff35bb4 Merge branch 'master' into feature/player-ui 2020-12-15 21:57:03 +01:00
c1c9dd1625 Version bump 2020-12-15 20:29:33 +01:00
ffa38cf4ff Readme fixes 2020-12-15 20:28:36 +01:00
3d64a08b08 Remove autoarPreventConditions — they're redundant with new way of calculating AR and, as of now, interfere with netflix' ultrawide workarounds 2020-12-15 20:13:23 +01:00
79462c320d Add anchor for player UI 2020-12-15 00:26:19 +01:00
7881ae5290 Update roadmap in changelog 2020-12-14 20:39:28 +01:00
2d6052d715 Update changelog 2020-12-13 23:59:28 +01:00
b4e8622af3 Add hint text in options -> add action popup 2020-12-13 23:31:27 +01:00
7c2cd5cf03 fix icon padding in notification 2020-12-13 23:30:52 +01:00
2a350f404a Remove some logging 2020-12-13 18:54:32 +01:00
6907f65439 Fix code that replaces notification UI so it no longer crashes restore() inside updatePlayerDimension() 2020-12-13 18:54:10 +01:00
5713037c4d Prevent notification UI from blocking website interface 2020-12-13 16:55:30 +01:00
cd26a1ceed Prevent page css from effecting CSS of notification popups (...ish) 2020-12-13 02:03:17 +01:00
c05eed44f0 Did a very bad thing (tm) to appease Chrome (bootstrap icons svg was copied directly into Icon component) 2020-12-13 01:11:52 +01:00
f27b3f1895 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/ini-1.3.7' 2020-12-12 00:38:59 +01:00
f4f69d4eaa Nest ultrawidify classes inside a css class that guarantees reset of global css 2020-12-12 00:38:51 +01:00
1badce511e add ms icons 2020-12-12 00:12:12 +01:00
dependabot[bot]
595109024a Bump ini from 1.3.5 to 1.3.7
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-11 20:46:40 +00:00
f55731e793 fix popup 2020-12-07 00:09:28 +01:00
6061a3efc5 Add DRM check for Chrome 2020-12-07 00:09:08 +01:00
b6cae36528 fix? 2020-12-07 00:07:59 +01:00
7d8f3912bb Fix extension popup in chrome 2020-12-06 21:57:16 +01:00
eb02865131 Sorta include browser-polyfill but not really
I'm still not rewriting legacy code, but the bRoWsEr iS uNdEfInEd bullshit in (browser || chrome).runtime call there in the notifications pushed some buttons. cc #114
2020-12-05 04:12:37 +01:00
0310e1e2de Finish notification system (for now) 2020-12-05 03:30:43 +01:00
12b15c58f8 LPT: injected components won't get CSS styles if you don't include them in manifest.json 2020-12-05 00:45:48 +01:00
4635dc4eec fix ui 2020-12-04 02:02:25 +01:00
0a5243837f Make buttons unselectable (prolly) 2020-12-04 00:54:03 +01:00
37eccb12f2 Fix normal-sized popup 2020-12-04 00:53:51 +01:00
84db9361f9 Fix default icon margins 2020-12-04 00:53:20 +01:00
3fd432f432 Extra styling for the button 2020-12-04 00:26:39 +01:00
d4947571a4 Try injecting notification UI 2020-12-03 01:35:48 +01:00
e8e9db2bbc Add scss imports 2020-12-03 01:35:34 +01:00
4ed998a9ab Fixes 2020-12-03 01:35:22 +01:00
7900bd18db Pointless changes to logging 2020-12-03 01:16:57 +01:00
357b7a0365 I removed this and stuff still works, therefore it must not have been important 2020-12-03 01:05:57 +01:00
145b237701 Additional logging / change logging 2020-12-03 01:05:39 +01:00
38ef7905c7 Fix a rookie mistake + rename the class 2020-12-03 01:03:29 +01:00
38641df28e add classes for notification UI.
The UI classes are split into "general UI" part — a base class that could potentially be used for proper in-player UI if we ever get to that point — and part that's specific to our notification requirements.
2020-12-03 00:34:50 +01:00
55fbd30699 Add notification UI component 2020-12-03 00:32:33 +01:00
5e4aca66c5 Fix icon css 2020-12-03 00:32:17 +01:00
8325e44a0e Plant a typescript seed into webpack config 2020-12-02 22:39:57 +01:00
a91ed7368f Add icon component 2020-12-02 22:39:43 +01:00
cda4b81554 Add bootstrap icons 2020-12-02 22:39:07 +01:00
be759c512a Version bumps 2020-12-01 01:49:55 +01:00
cfde1ee58c Update changelog with stuff that was done today 2020-12-01 01:48:52 +01:00
db866bf8d8 Add support for wakanim.tv (#113) 2020-12-01 01:48:31 +01:00
d32c083406 Fix quick setting dialogue when extension is in extension overflow menu (#119) 2020-12-01 01:46:53 +01:00
e56551c79d Remove redundant code 2020-12-01 00:22:22 +01:00
97099ece1c Merge branch 'vue3' 2020-12-01 00:21:29 +01:00
f05f37841a Fix logger UI 2020-12-01 00:20:10 +01:00
dbc0c8c741 Fix 'about' section of the options page 2020-11-30 00:50:13 +01:00
2ab01c1013 optionally chain _all_ the things. Again. 2020-11-30 00:38:38 +01:00
ada7e69abd Fix options and popup pages 2020-11-30 00:37:45 +01:00
27e03115d2 Migrate to vue3. Debug popup is broken ATM 2020-11-30 00:17:46 +01:00
1b6d20fa0b Turns out vue update updated jack shit, so optional chaining operators are gonna disappear from vue templates again 2020-11-22 22:48:07 +01:00
aa186da1bd Bump vue 2 2020-11-22 22:46:49 +01:00
a6a09a10b5 Add optional chaining operators back
This reverts commit e25a0a954c.
2020-11-22 22:46:49 +01:00
e25a0a954c Revert "optionally chain _all_ the things"
This reverts commit 1858e092a9.
2020-11-22 22:17:28 +01:00
09f203dd64 Bump vue two major versions #yolo 2020-11-22 22:15:43 +01:00
1858e092a9 optionally chain _all_ the things 2020-11-22 22:12:52 +01:00
e5f2bde6bc add custom setter for settings 2020-11-22 22:12:37 +01:00
0848b2fe67 Some things break youtube, so baseClass is split off into its own component differently 2020-11-06 00:03:11 +01:00
691675bdc2 Awaitable responses to injectCss (& the rest of the gang) 2020-11-05 23:56:41 +01:00
1e70ea51cf This is handled via base class 2020-11-05 23:56:06 +01:00
f8e3a39620 Fix setBaseClass function 2020-11-05 23:55:42 +01:00
6b91444f3c Set, unset base class functions 2020-11-05 23:12:03 +01:00
4f7fc75d6b Version bump 2020-10-25 22:24:30 +01:00
51735ad8c2 What's new 2020-10-25 22:11:24 +01:00
78c34c77e0 Fix for cruncyhroll (#115) 2020-10-25 22:07:38 +01:00
65dacee829 Version bump 2020-10-21 23:56:38 +02:00
288f2334f6 Changelog 2020-10-21 23:55:42 +02:00
43a86a7495 Destroy old pageInfo instance when initializing WU 2020-10-21 23:52:49 +02:00
28c12861e5 Change action handler 2020-10-21 23:52:16 +02:00
7fb9aad0d0 misc fixes 2020-10-21 23:51:58 +02:00
48167623ff Force user styles a bit harder 2020-10-21 21:42:22 +02:00
43b136ca46 Fix crashing when checking player size 2020-10-21 21:26:00 +02:00
ec83830e78 Force zero margins on <video> element.
This should fix issues with alignment on twitch and hulu, et al.
2020-10-21 19:49:26 +02:00
7093236237 fix some issues related to panning 2020-10-21 19:48:56 +02:00
c7d133ba3d ensure aspect ratio only gets changed on aspect ratio changes 2020-10-21 19:48:04 +02:00
47475ac597 document aspect ratio enum 2020-10-21 19:46:25 +02:00
4f478a4667 Calculate "real" displayed side of video instead of relying on offsetWidth 2020-09-23 00:23:24 +02:00
b55d9dd7b9 fix? 2020-09-23 00:19:42 +02:00
02fa4e2fe8 style fixes 2020-09-20 12:26:03 +02:00
e5206b524f Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/bl-3.0.1' 2020-09-20 12:25:24 +02:00
dependabot[bot]
5695534a9a Bump bl from 3.0.0 to 3.0.1
Bumps [bl](https://github.com/rvagg/bl) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/rvagg/bl/releases)
- [Commits](https://github.com/rvagg/bl/compare/v3.0.0...v3.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-02 16:26:01 +00:00
ca6083b0ee version bump 2020-07-25 13:02:33 +02:00
8d29e7b5bb fix prepare-amo-source script 2020-07-25 13:02:25 +02:00
7f9766ff95 Add .nvmrc 2020-07-25 12:57:30 +02:00
5098c45174 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/lodash-4.17.19' 2020-07-25 12:54:10 +02:00
6ca6fcd10c update libraries 2020-07-25 12:53:58 +02:00
86a184fe29 Remove build for old edge 2020-07-25 12:43:15 +02:00
19f2ef7d68 Yet another readme update 2020-07-25 12:42:04 +02:00
dependabot[bot]
74ea6fa238 Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-18 06:59:55 +00:00
87a65d4af2 Updated readme 2020-06-28 21:51:26 +02:00
2eec8b3ef9 readme update & version bump 2020-06-18 22:27:52 +02:00
cfa0c4e2c8 Update build scripts 2020-06-18 22:22:43 +02:00
ced35084ba Reproducibility test 2020-06-18 22:12:45 +02:00
20346a784e Re-allow minification. Remove source maps from prod build. 2020-06-18 22:12:28 +02:00
4f6f946245 properify readme some more 2020-06-16 21:41:16 +02:00
16a2a424d2 Remove redundant info from README-AMO 2020-06-16 21:40:03 +02:00
2aea741372 Fixes for README-AMO preparation 2020-06-16 21:35:46 +02:00
53073ecdfa README-AMO updates 2020-06-16 21:35:21 +02:00
8216965533 Variable fix 2020-06-14 23:46:22 +02:00
88ce945a47 Include node/npm/linux versions in README-AMO.md, rename README-AMO.md to README.md when including it into the archive 2020-06-14 23:41:02 +02:00
fef30ce3e9 Fix script that packs source code for AMO 2020-06-14 21:48:04 +02:00
0cdbb19b7a Readme+changelog update 2020-06-05 00:59:44 +02:00
69e44c4964 version bump 2020-06-05 00:59:12 +02:00
d59a78b039 Hopefully this fixes #104. Turns out 'location.host' and 'location.hostname' aren't quite interchangeable 2020-06-04 22:47:04 +02:00
e7957b6503 Extra logging 2020-06-04 22:44:53 +02:00
3b4b52f6a1 logging fixes 2020-06-04 22:15:32 +02:00
a4bca8af45 Additional fixes for #101 — if illegal video dimensions are detected, tell VideoData to reset/reapply aspect ratio when video starts playing again 2020-06-04 21:51:22 +02:00
cc53df5999 Youtube player detection goes back to auto by default 2020-06-02 01:05:03 +02:00
a271b4d63e fix observer 2020-06-02 00:52:23 +02:00
5c73f97c54 Add player dimension sanity check to resizer 2020-06-01 23:54:42 +02:00
ecc65676f2 cosmetic fix 2020-06-01 23:54:24 +02:00
619d89c4f5 Break videoData setup into two stages for improved stability 2020-06-01 23:54:10 +02:00
fe66c9dfbc Diversify logging: in addition to console.log, use .warn .info .error as well 2020-06-01 23:53:03 +02:00
95d8d8a8cb Fix minor issue 2020-06-01 23:52:29 +02:00
ee7d958224 Fix settings 2020-05-29 22:50:39 +02:00
88a3298cfa fix launch.json 2020-05-29 22:50:07 +02:00
4a761efaed Find all blacklisted origins in stack 2020-05-29 22:06:24 +02:00
fc8d011758 Additional fixes 2020-05-17 02:30:16 +02:00
3708b99186 version bump 2020-05-17 02:17:59 +02:00
291c4fdd4d changelog update 2020-05-17 02:17:52 +02:00
7d8758d1ee Do old frame cleanup with polling 2020-05-16 22:50:29 +02:00
9019963597 fix single video control not working 2020-05-16 22:02:06 +02:00
7bb3d079f7 Fix issue with chrome not hiding frame/video numbers 2020-05-16 21:35:24 +02:00
84470846ef Typo fixes & addign words to dictionary 2020-05-16 20:52:37 +02:00
c17e6f0f6d Discern popup of dev/nightly/testing versions from stable version 2020-05-16 20:31:56 +02:00
a0031be90a unfuckywucky the icons 2020-05-14 00:01:18 +02:00
ed0e51114c Change timeouts again 2020-04-30 00:40:12 +02:00
1646958449 version bump 2020-04-30 00:14:36 +02:00
e1b062f19e increase number of retries 2020-04-30 00:14:26 +02:00
ff0dc23d5d Fix build generate script 2020-04-29 23:21:32 +02:00
cb91e2a09f Forgot to bump version in manifest 2020-04-29 23:03:22 +02:00
103ecf7a83 Version update stuff 2020-04-29 22:55:20 +02:00
ccde079aee If video dimensions are invalid, wait a little and check again before performing the crop calculation 2020-04-28 03:05:55 +02:00
10f1bae7de move sleep function to a separate fire 2020-04-28 03:04:15 +02:00
dfdc26fe2b fix font imports 2020-04-28 00:35:41 +02:00
06cc9220c2 scope styles for jsonElement 2020-04-26 16:26:15 +02:00
a7f654650d Logging when file loads 2020-04-13 15:20:29 +02:00
ff525f159b try rescoping 2020-04-13 14:35:04 +02:00
44eef78d29 whitespace removal 2020-04-13 14:31:01 +02:00
7c80da3b5a add ms certification notes 2020-03-22 02:43:45 +01:00
Tamius Han
d2a6dfe55d Merge pull request #98 from tamius-han/dependabot/npm_and_yarn/acorn-6.4.1
Bump acorn from 6.4.0 to 6.4.1
2020-03-15 20:41:58 +01:00
Tamius Han
7873777b96 Merge pull request #99 from tamius-han/stable
Merge branch 'master' into stable
2020-03-15 20:41:02 +01:00
1268c2b0cc Merge branch 'master' into stable 2020-03-15 20:39:31 +01:00
d6e6c4ad40 Don't include some files in final package 2020-03-15 20:28:38 +01:00
0272597bda hide zoom&panning options as they don't work anyway 2020-03-15 20:20:04 +01:00
2e7b7f6c30 Popup: fix 'tab' sidebar max width 2020-03-15 20:19:48 +01:00
e447b39beb Delete invalid keys from manifest.json for Chromium-based browsers 2020-03-15 20:19:24 +01:00
9df8bdf3b9 manifest.json fix 2020-03-15 20:01:45 +01:00
95676d5343 Changelog 2020-03-15 19:56:52 +01:00
309f738d25 version bump 2020-03-15 19:56:47 +01:00
d2bd215877 Update AMO readme 2020-03-15 19:47:46 +01:00
c2ebe34ab6 Disable logging + extra notes 2020-03-15 19:47:38 +01:00
c83f8530f8 emit/handle loggingEnded 2020-03-15 19:16:27 +01:00
e9795f4a00 Add new thing to vue store 2020-03-15 19:14:33 +01:00
dependabot[bot]
d63197b13a Bump acorn from 6.4.0 to 6.4.1
Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-15 16:31:13 +00:00
72a3bba8ad Auto-show logger popup is logging to file is enabled 2020-03-13 00:29:19 +01:00
f0cc710621 fix imports 2020-03-13 00:28:41 +01:00
49ca4b64e3 forwarding logs to UI 2020-03-13 00:28:34 +01:00
06a2f67759 Fix saving logs 2020-03-13 00:19:39 +01:00
783684570c actually wait, we still need logger to save to vuex 2020-03-13 00:17:14 +01:00
5998b6c0b2 Functions for sending to specific frame/port 2020-03-13 00:13:00 +01:00
6a3cc8e03e Use sendmessage instead of vuex store in logger 2020-03-13 00:12:42 +01:00
a629bbeab7 Logger cosmetics 2020-03-12 23:36:06 +01:00
7e74aa2c3b Add ignoreKeys property to JsonEditor 2020-03-12 23:35:52 +01:00
9a5ccf1602 JsonEditor uses monospace 2020-03-12 00:49:40 +01:00
d7d0f69a58 Update logger settings from JsonEditor 2020-03-12 00:49:24 +01:00
217206ca1e Change how strings and text are editable (submit on enter), colorify json 2020-03-12 00:37:57 +01:00
a7fa66b840 Prevent typing in logger popup from triggering page shortcuts 2020-03-12 00:37:15 +01:00
4df771aae1 Remove pre 2020-03-12 00:36:34 +01:00
77f3ce0e39 Display logger conf with json components 2020-03-11 23:12:29 +01:00
b353e6f34d Fix json components 2020-03-11 23:12:18 +01:00
e9dfd834f7 Load UI css with extension 2020-03-11 23:11:55 +01:00
c9b354e06a Add json structure editor 2020-03-11 00:15:53 +01:00
7b845553e2 remove player images 2020-03-10 21:00:54 +01:00
fe72a4fdec Fonts: remove .woff (since css only loads woff2 anyways), extrabold, heavy 2020-03-10 20:17:17 +01:00
6e6b3522fe Merge branch 'stable' 2020-03-10 00:04:42 +01:00
bf0d3b99cf Version bump 2020-03-10 00:04:09 +01:00
7d4e4fc520 Fix disney+ a bit more thoroughly 2020-03-10 00:03:12 +01:00
e4480a91ef Merge branch 'master' into stable 2020-03-09 22:36:26 +01:00
e2d393d500 Fixes for netflix, disney+ 2020-03-09 22:36:17 +01:00
ce6a22d31b Merge branch 'master' into stable 2020-03-09 22:08:20 +01:00
85290ae832 Version update 2020-03-09 22:07:54 +01:00
3f51048c1a Patch notes 2020-03-09 22:06:34 +01:00
a81c5765e1 Minor fixes for chrome 2020-03-09 21:41:31 +01:00
cf1cc3d87a Fix logger popup initialization. 2020-03-09 21:29:39 +01:00
2a1419eb53 Fix uw script 2020-03-09 21:29:24 +01:00
0edb5d5c4f add words to dictionary 2020-03-09 21:29:10 +01:00
5b417d3f47 cosmetic fix 2020-03-09 21:29:00 +01:00
eee7d622b8 Revert logging oopsie whoopsie 2020-03-09 19:41:15 +01:00
bbc4247893 Don't overwrite existing ports on connect 2020-03-09 19:39:24 +01:00
26757c0b2d Remove redundant code 2020-03-09 19:11:23 +01:00
b3fce146f5 Fix port naming 2020-03-09 19:11:08 +01:00
80c14f72be comment out dead imports and code 2020-03-08 18:47:01 +01:00
6ea2b0488d Fix console errors 2020-03-08 18:43:53 +01:00
5cc90ea368 Fix problems with uw-ui 2020-03-08 18:38:36 +01:00
d8181431ff Block show-logger until ui is initiated 2020-03-08 18:38:09 +01:00
e681a64f55 remove unnecessary code 2020-03-08 18:34:30 +01:00
92c4ba0c69 Remove paginfo and command declarations out of comms 2020-03-08 17:13:50 +01:00
c26744b9d9 handleMessage _always_ tries to execute command, even if it has forwarding flags set. Command is executed before forwarding. 2020-03-08 16:49:20 +01:00
4c41601319 spin UI to a separate file 2020-03-08 16:45:46 +01:00
210290e7f4 Merge branch 'master' into stable 2020-03-03 00:36:34 +01:00
95aec0f68c remove install/first time tab from webpack, as it's not used yet 2020-03-03 00:36:24 +01:00
a600f30ff8 Merge branch 'master' into stable 2020-03-03 00:34:15 +01:00
ec046767c7 Changelog update 2020-03-03 00:33:55 +01:00
f86ef5bda6 Version update 2020-03-02 22:35:41 +01:00
77ee6be218 Fix getting extension version in the popup 2020-03-02 22:35:34 +01:00
1c978e61b7 Fix the bug with Settings.fixSiteSettings() 2020-03-01 21:52:57 +01:00
aba099d577 Disable unnecessary logging 2020-03-01 21:52:47 +01:00
33e05014fd flex.css -> flex.scss for dat scoping 2020-03-01 20:24:08 +01:00
c5f6868a63 Make sure aspect ratio makes sense before setting it. Don't set NaN for aspect ratio, that tends to break things 2020-03-01 16:55:52 +01:00
4b177a9724 initialize content script vue/ui only if there's a need 2020-02-26 00:45:35 +01:00
9a2261faed whitespace 2020-02-26 00:45:06 +01:00
d009b53dba update webpack to include new install/update pages 2020-02-26 00:44:28 +01:00
acfa833203 Some stuff for easier logging configuration - wip 2020-02-26 00:43:11 +01:00
4c0aca48fd Add "first time" and "updated" pages templates. 2020-02-26 00:42:41 +01:00
66169ddeb1 Remove scheduleGc to avoid webNavigation 2020-02-14 21:45:31 +01:00
72b28918e9 Try disabling minimization 2020-02-14 20:55:20 +01:00
7eca642da3 Let's take away 'tabs' permission, since activeTab seems to do the job just fine 2020-02-14 20:29:38 +01:00
4fa1cb5cbc Merge branch 'master' into stable 2020-02-11 20:01:33 +01:00
18a3a2162a Changelog and version update stuff 2020-02-11 20:01:10 +01:00
6054da346d Merge branch 'master' into stable 2020-02-11 20:00:04 +01:00
ef52eec860 Merge branch 'master' into stable 2020-02-11 19:59:33 +01:00
c88247df25 Fix problem with video sometimes being offset to the side 2020-02-11 19:54:41 +01:00
f028af8b98 change single quotes to double quotes for json 2020-02-11 19:36:49 +01:00
51526b5dcc Version bump 2020-02-09 02:11:37 +01:00
2ead2f8e41 Changelog 2020-02-09 02:11:28 +01:00
c4914b4e5b Improve handling of change listener on settings, fix bug that prevented callback from triggering 2020-02-09 01:16:46 +01:00
a5685fa420 Improve logging of storage listener in logger 2020-02-09 01:14:05 +01:00
f53faac344 Make logger thing a proper json, not just javascript object 2020-02-09 01:13:29 +01:00
c9f227c0f9 This appears to do exactly nothing? 2020-02-09 00:28:35 +01:00
470f576b41 Fix build-zip script 2020-02-08 01:05:05 +01:00
8945226d24 Fixes for Edge 2020-02-08 00:41:10 +01:00
6b7f41ecf8 update readme for AMO 2020-02-08 00:38:40 +01:00
dd74725c0a fix run scripts, but for real. Fix related stuff as well 2020-02-08 00:38:09 +01:00
9338711d4c update gitignore 2020-02-08 00:12:49 +01:00
ddf8c3210f typo fix 2020-02-08 00:09:07 +01:00
806a889726 Gotta have separate dist folders for each browser 2020-02-08 00:08:07 +01:00
0ab21f8360 Remove unnecessary scripts, 'npm run dev' builds both chrome and ff builds 2020-02-08 00:07:34 +01:00
7f806a55af Install concurrently package 2020-02-07 23:51:35 +01:00
31ed290390 fix webpack, don't include useless files (ext/lib and ext/conf) in build
Since files in /lib and /conf use optional chaining, and since optional chaining is only supported through the magic of babel plugins, this is one sure way to fail validation
2020-02-07 00:07:28 +01:00
2788b9e439 fix imports for chrome 2020-02-07 00:06:12 +01:00
355108e0ef Ensure things don't crash on undefined settings 2020-02-06 23:53:31 +01:00
0eb3840221 Move easter eggs around 2020-02-06 23:52:58 +01:00
6013fd68ff Add easter eggs 2020-02-06 23:09:22 +01:00
63922d5744 Changelog updates 2020-02-06 23:06:37 +01:00
f721c795e6 Version bump 2020-02-06 23:02:28 +01:00
006e684f17 Logger fixes 2020-02-06 22:59:57 +01:00
e529239c8d save as .log instead of .json 2020-02-06 22:59:43 +01:00
21e24cc226 Finalize logging 2020-02-06 22:29:59 +01:00
435f2890c4 Logger is half functional 2020-02-05 00:39:16 +01:00
179614bd85 Finish logging ... sorta 2020-02-04 23:31:50 +01:00
f0f02032cc cosmetic fixes 2020-02-04 23:15:06 +01:00
8a70fc5f81 fix export 2020-02-04 23:14:48 +01:00
ef0a802c16 move export log to file to its own file 2020-02-04 22:05:29 +01:00
22bcd02afc Add show/hide popup buttons to 'report a problem' popup tab. Removed everything else re: logging from there. 2020-02-04 00:57:30 +01:00
bd2fa787e1 work on logger popup 2020-02-04 00:56:48 +01:00
87e65a99d4 Messaging fixes 2020-02-04 00:56:31 +01:00
0cd644a38a fix ui overflow 2020-02-03 22:14:07 +01:00
78364472ad Avoid logging too much unnecessary stuff 2020-02-03 22:13:03 +01:00
8eed3431c8 Show log results in popup 2020-02-02 00:57:54 +01:00
f5bde78378 add webext polyfill 2020-02-02 00:57:34 +01:00
390b788b61 add css to manifest 2020-02-01 17:48:49 +01:00
7cb77a7a58 whitespace 2020-02-01 17:48:40 +01:00
a1395da957 abandon ghetto css 2020-02-01 17:48:31 +01:00
983c7747cc Create dummy logger UI 2020-01-31 01:03:06 +01:00
b1538e9a8c logger fixes and refactorting 2020-01-30 23:18:30 +01:00
646d98588a logging fixes 2020-01-30 23:17:43 +01:00
5890192751 sleep() to utils 2020-01-30 23:15:08 +01:00
e6b1370f71 get a better package for decycle 2020-01-30 23:14:56 +01:00
6aba6e0277 add decycle to dictionary 2020-01-30 23:14:40 +01:00
89a459e42b Include json-decycle 2020-01-30 21:25:23 +01:00
2ad5a7b205 consistent naming of 'eject-css' command 2020-01-30 01:07:15 +01:00
d2fdbe178c Implement finish logging and connect things to export 2020-01-30 01:07:00 +01:00
1cdf246919 Refactor comms server a little bit 2020-01-30 01:06:21 +01:00
810fbbdc97 fix bug in actionhandler that causes log to stop logging 2020-01-30 01:06:02 +01:00
0abd4fc776 Add nightly build links to readme 2020-01-29 23:53:52 +01:00
97d97e022b Add onLogEnd callback to logger 2020-01-29 23:38:52 +01:00
78ff7d7e40 Fix logging 2020-01-29 01:36:23 +01:00
1899f486e1 Settings should only watch for uwSettings storage changes, Logger should only monitor for uwLogger changes 2020-01-29 01:36:09 +01:00
9ba1afebee Use optional chaining 2020-01-28 23:34:36 +01:00
c8d943da0b Fix logging bugs, add mousemove to blacklist by default 2020-01-28 23:11:59 +01:00
f5f3e2754f Update npm packages & add optional chaining 2020-01-28 23:11:15 +01:00
0cbd62352e [Logger] Track origin of the call stack. Allow blacklisting of some periodic calls 2020-01-28 01:27:30 +01:00
3e565ddbfc fix logger in popup 2020-01-28 01:26:40 +01:00
c84900def0 dictionary update 2020-01-28 01:26:20 +01:00
1588ebd274 Don't restore ar if player dimensions haven't changed 2020-01-27 23:42:55 +01:00
691f45fbcc remove log to console 2020-01-27 23:42:35 +01:00
c62033fb28 typo fixes and whitespace/console.log removals 2020-01-27 23:42:17 +01:00
dfabfaad48 pass CHANNEL to process.env for extension to use 2020-01-27 23:35:38 +01:00
f770b0dbf1 fixes for logger 2020-01-27 23:20:38 +01:00
f3e5aeb9e6 Fix logger crashing the extension 2020-01-27 22:32:38 +01:00
f1d2881359 Add logger commands to comms server 2020-01-21 00:41:18 +01:00
8c78715271 additional logger functions 2020-01-21 00:41:06 +01:00
8b648fb4d7 Rework of command handling in CommsClient 2020-01-21 00:40:56 +01:00
46fc868109 fix settings for logger 2020-01-20 20:07:18 +01:00
d633cb3b97 Only allow in-code log overrides if we're running in dev 2020-01-20 20:07:01 +01:00
5372acdf07 Logging changes for build and deploy 2020-01-20 20:06:36 +01:00
9234b86e46 Create dist-zip after deletion 2020-01-19 21:32:49 +01:00
6031a99af7 fix build 2020-01-19 21:08:34 +01:00
6f5768567f logging in build-zip 2020-01-19 20:27:05 +01:00
65b9822e24 mkdir recursive 2020-01-19 20:21:11 +01:00
d78a83c4e3 re-enable FF builds (without signing) 2020-01-19 20:16:34 +01:00
fe85f6a7a5 change directory for builds 2020-01-19 20:13:47 +01:00
6b78e5652e fix typo 2020-01-19 20:05:30 +01:00
bbef2aa2e7 manually copy built files via ssh 2020-01-19 20:03:03 +01:00
13f4dcb215 fix jenkins build script 2020-01-19 19:43:32 +01:00
ff82807130 change package.json stuff 2020-01-16 21:49:13 +01:00
c7980ab892 Update github username 2020-01-16 01:02:19 +01:00
753a9348f4 Add rudamentary logger section to 'about' tab of popup 2020-01-16 01:01:04 +01:00
79e781dcd8 Support static save, load, subscribe to logger settings changes 2020-01-16 01:00:47 +01:00
6b9bf1b70f New logging property! 2020-01-16 01:00:12 +01:00
76b1da1b1c Change email template in hopes for illiterate twats to not edit or remove my precious process.env and generated version number
smh my head
2020-01-15 23:37:34 +01:00
7fdb15821a Changelog updates 2020-01-15 22:23:22 +01:00
d2c1b2de3e Get current site more reliably (hopefully) 2020-01-15 21:20:34 +01:00
d6201dc2ac Add override to log everything 2020-01-15 21:09:12 +01:00
0f6b707d69 Merge remote-tracking branch 'origin/master' 2020-01-06 21:40:10 +01:00
90c1ba0b6c Merge branch 'HEAD' into stable 2020-01-06 21:39:23 +01:00
73872d4188 Add timeout to logger 2020-01-06 21:38:10 +01:00
936cb78b26 typo fix 2 2020-01-06 21:38:10 +01:00
2dca756b20 typo fix 2020-01-06 21:38:10 +01:00
3d2bbe742b npm appeasal 2020-01-06 21:38:10 +01:00
bc51505727 Version bump, changelog 2020-01-06 21:38:10 +01:00
c8418f0373 Fix extension failing to load 2020-01-06 21:38:10 +01:00
39a7a8187e typo fix 2 2020-01-04 15:46:43 +01:00
d66cfd430d typo fix 2020-01-04 15:45:28 +01:00
d9430dc90d npm appeasal 2020-01-04 15:45:21 +01:00
1c1d31e563 Version bump, changelog 2020-01-04 15:43:11 +01:00
bf8d133962 Fix extension failing to load 2020-01-04 15:34:54 +01:00
0920c234d0 Merge branch 'master' into stable 2020-01-04 02:47:56 +01:00
4cd0eac01b Changelog update 2020-01-04 02:47:09 +01:00
c08ffa9c58 disney+ conf patch 2020-01-04 02:37:00 +01:00
410cd4d7f3 Fix invalid site settings on settings.save 2020-01-04 02:36:46 +01:00
3d7a47662e minor fix for autoar 2020-01-04 02:04:32 +01:00
e37ad4ba7a Version bump (todo: release notes) 2020-01-04 00:42:13 +01:00
377a58c427 Merge branch 'master' into stable 2019-12-12 20:12:34 +01:00
0c6355e370 Merge branch 'testing' 2019-12-06 00:17:38 +01:00
b9e8d111e6 Version update stuff 2019-12-06 00:17:20 +01:00
73b1a57749 Added extra stretch/squish options, hopefully fixed disney+, fixed bug that prevented removal of actions added by user 2019-12-06 00:17:09 +01:00
0801c122e2 disable copy for a bit, we're doing that with plugin now 2019-12-04 18:26:17 +01:00
e58f5c4b62 Merge branch 'master' into testing 2019-12-04 18:25:58 +01:00
4bfb116d45 fix for build override? 2019-12-04 18:04:46 +01:00
96d7456ea1 Merge branch 'testing' 2019-12-04 18:02:38 +01:00
18cf4e5d0f Add logging to build script 2019-12-03 22:39:07 +01:00
6283bd74e5 Fix script 2019-12-03 22:30:26 +01:00
585ad1f525 Merge branch 'master' into testing 2019-12-03 22:19:14 +01:00
33d9bbe00b +x 2019-12-03 22:19:00 +01:00
8b066c3692 Merge branch 'master' into testing 2019-12-03 22:12:02 +01:00
291699b925 Build script moved to file 2019-12-03 22:11:29 +01:00
14a2233ff2 Merge branch 'testing' 2019-12-03 02:21:44 +01:00
cc6d08e452 Readme update to trigger jenkins build 2019-12-03 02:20:59 +01:00
1cd2af7c58 Merge branch 'master' into testing 2019-12-03 02:16:12 +01:00
a1b89e0fc2 This is here mostly to test CI (master commit shouldn't build, merge to testing should) 2019-12-03 02:11:16 +01:00
9036bb5810 Merge branch 'master' into testing 2019-12-03 02:07:40 +01:00
5d200805f0 build-zip should get data from /dist instead of /src 2019-12-03 02:07:15 +01:00
30d6338d76 Separate testing and nightly 2019-12-03 02:01:29 +01:00
bd38e28610 fix 2019-11-29 01:53:23 +01:00
967a5bf87b we don't have background.js file 2019-11-29 01:48:30 +01:00
2675960aa0 fix key name 2019-11-29 01:39:01 +01:00
289baf73e5 Force player re-detection when setting aspect ratio 2019-11-29 01:33:58 +01:00
5a6195d36d fix permissions 2019-11-29 01:27:24 +01:00
d359b60bc5 don't remain cd'd into folder 2019-11-29 01:26:52 +01:00
1581e3382c Add crx build file 2019-11-29 01:25:40 +01:00
6355e31004 Also ignore kate swap files, they're garbage we don't need on git 2019-11-29 01:00:59 +01:00
ec36ca1bc8 Version number is already in process.env on server, no need for this 2019-11-29 01:00:24 +01:00
51d0cbf16f Include build number in version scripts 2019-11-29 00:55:22 +01:00
db5d69d92f fix build 2019-11-29 00:31:20 +01:00
470f5481ee fix? 2019-11-29 00:15:48 +01:00
e51a2f4193 fix build config 2019-11-29 00:12:31 +01:00
62b986575c Zip: collapse dashes and spaces into single dash 2019-11-28 23:52:55 +01:00
4be2d902b8 Merge remote-tracking branch 'origin/master' 2019-11-28 23:37:45 +01:00
709cb7763c prepare stuff for CI nightly builds 2019-11-28 23:37:26 +01:00
f469c29cc8 Readme update, pt. 2 2019-11-25 23:45:45 +01:00
cc36305568 Readme update 2019-11-25 23:42:22 +01:00
b43747a59c Merge branch 'master' into stable 2019-11-05 00:09:20 +01:00
fcd9deac6f Version update 2019-11-05 00:09:08 +01:00
97ef4c2fec Changelog updates 2019-11-05 00:08:27 +01:00
0eadb1aac6 Don't set aspect ratio pointlessly 2019-11-04 23:53:28 +01:00
5fc867b4a3 Fix for vk 2019-11-04 23:53:08 +01:00
414bf7b36f Update logger output 2019-11-04 23:52:37 +01:00
4fc68db7f8 Disable logging 2019-11-04 23:50:54 +01:00
08460a923d Fix logger 2019-11-04 22:14:41 +01:00
5f4abcec1f version bump 2019-11-02 22:43:11 +01:00
1157a8a8e2 Update readme 2019-11-02 22:42:36 +01:00
b2f6a1fddd Auto-collect extension/browser info in mailto/reddit compose links (and hopefully fix them) 2019-11-02 21:58:55 +01:00
b6ff740b84 Merge branch 'master' into stable 2019-11-02 02:47:59 +01:00
0e09ae771c Update readme 2019-11-02 02:45:30 +01:00
970b9b6b1d Reset panning on aspect ratio change. 2019-11-02 02:45:24 +01:00
83a4dd08ac remove console litter 2019-11-02 02:21:21 +01:00
f806d154ee Fix panning 2019-11-02 02:20:09 +01:00
b128f97a7c Fixed readme 2019-11-02 01:11:57 +01:00
dbc35ccbd1 Patching for v4.4.0 2019-11-02 01:05:36 +01:00
5becf0bdf0 Version bump is gonna be a bit more major 2019-11-01 23:51:51 +01:00
04c33956a1 changelog update 2019-10-30 17:47:15 +01:00
42d85f75f7 Logging, prepare code for when browsers finally implement ArrayBuffer.transfer() 2019-10-29 18:15:46 +01:00
18f0c7540b Fix incorrect crop resets 2019-10-29 18:15:10 +01:00
1c4c4d5652 update test videos 2019-10-29 00:38:06 +01:00
ab42557283 Fixes for crop mode persistence on the popup side of things 2019-10-28 22:10:15 +01:00
5e20925ec5 Fix fit to width / fit to height for portrait monitors 2019-10-28 20:33:40 +01:00
9b8e500927 Prevent action on contentEditable divs. 2019-10-27 23:37:13 +01:00
83d84e5a93 Fix popup not displaying 2019-10-27 23:20:45 +01:00
470a7e8883 Fix video fit on portrait orientations 2019-10-27 22:11:07 +01:00
2422cec1e9 update formatting 2019-10-27 22:08:48 +01:00
bde7eb6327 Fix autodetecting not starting 2019-10-27 16:51:47 +01:00
6af771e1ff Restrict which kind of mutation events are allowed to trigger player refresh 2019-10-27 16:51:31 +01:00
828ded6b29 Fix overly aggressive forced player refreshes 2019-10-27 16:48:05 +01:00
f05a0813d2 Fix minor issues 2019-10-27 00:10:49 +02:00
854bc05d14 Pass set-ar-persistence between CommsClient and pageInfo 2019-10-26 02:38:47 +02:00
cda5d87735 Display new options in popup, update ExecAction 2019-10-25 00:59:02 +02:00
d0058cedb9 Changes to how crop mode persistence works, extra persistence options 2019-10-24 23:27:43 +02:00
d5a6e8d707 change reload prevention to something a bit less contrived 2019-10-24 21:13:45 +02:00
8fde5bb3d6 Implement crop persistence in content script 2019-10-24 00:45:11 +02:00
ab06f0bd41 Add saveWithoutReload() to settings.js 2019-10-24 00:44:27 +02:00
f652bce8fe Prepare version update stuff, readme updates 2019-10-23 20:01:11 +02:00
261f9a6b8d Use event.code instead of event.keyCode. Add key codes to default settings 2019-10-23 19:34:58 +02:00
cd4e3768e2 Make compiler a bit happier. 2019-10-22 01:40:46 +02:00
4a9ef1f9db Fix 'clear shortcut' button (probably) 2019-10-22 01:40:38 +02:00
3768575bad Allow default keyboard shortcuts to work on non-ASCII layouts 2019-10-22 01:33:56 +02:00
c28651d9f7 Merge branch 'master' into stable 2019-10-20 21:12:43 +02:00
fdc4fc8dc9 fix the fix 2019-10-20 21:11:17 +02:00
85fa314bb4 change some variable names for readability 2019-10-20 21:11:09 +02:00
7a9c341bf8 Version update stuff 2019-10-20 14:13:58 +02:00
cec427d9ab fix for twitch 2019-10-20 14:09:19 +02:00
a461d8fa71 test videos update 2019-10-20 14:03:09 +02:00
b53276da62 Merge branch 'master' into stable 2019-09-28 00:14:21 +02:00
2d6a020c5b Version update stuff 2019-09-28 00:14:03 +02:00
afe7b8e692 minor rework of settings options 2019-09-28 00:13:50 +02:00
408b771656 Fix settings page 2019-09-27 22:06:28 +02:00
f784eedd3f Merge branch 'master' into stable 2019-09-26 01:24:20 +02:00
e7f8ca058d Merge branch 'master' into stable 2019-09-22 02:49:53 +02:00
5171c3129b Merge branch 'master' into stable 2019-09-22 02:40:14 +02:00
2f76a90b29 Merge branch '4.2.4' into stable 2019-09-01 03:55:21 +02:00
8e21bb8f92 Merge branch '4.2.4' into stable 2019-09-01 01:52:34 +02:00
50e0d7aa59 Merge branch '4.2.4' into stable 2019-08-31 22:55:03 +02:00
a04b3ccecf Merge branch '4.2.3' into stable 2019-08-28 18:29:39 +02:00
335 changed files with 50375 additions and 19149 deletions

View File

@ -1,12 +1,13 @@
{ {
"plugins": [ "plugins": [
"@babel/plugin-proposal-class-properties"
], ],
"presets": [ "presets": [
["@babel/preset-env", { ["@babel/preset-env", {
"useBuiltIns": false, "useBuiltIns": false,
"targets": { "targets": {
"esmodules": true, "esmodules": true
}, }
}] }]
] ]
} }

14
.gitignore vendored
View File

@ -2,6 +2,16 @@ old/
build/ build/
/node_modules /node_modules
/*.log /*.log
/dist /dist*
/dist-zip
/uw-git_keys /uw-git_keys
/untracked-assets
*.pem
*.kate-swp
src/res/img/git-ignore/
test/debug-configs/
debugging-resources/

View File

@ -1,23 +0,0 @@
image: node:current
cache:
paths:
- node_modules/
- .yarn
stages:
- prep
- build
- pack
install deps:
stage: prep
script: yarn install
build:
stage: build
script: npm run build
create zip:
stage: pack
script: npm run build-zip

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
20.19.6

8
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"recommendations": [
"hollowtree.vue-pack",
"msjsdiag.debugger-for-chrome",
"firefox-devtools.vscode-firefox-debug",
"msjsdiag.debugger-for-edge"
]
}

65
.vscode/launch.json vendored
View File

@ -1,23 +1,52 @@
{ {
// Use IntelliSense to learn about possible attributes. // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"type": "chrome",
"request": "attach",
"name": "Attach to Chrome",
"port": 9222,
"urlFilter": "http://*/*",
"webRoot": "${workspaceFolder}"
},
{
"type": "firefox",
"request": "attach",
"name": "Attach (firefox)",
"pathMappings": [
{ {
"name": "Launch addon", "url": "webpack:///ext",
"type": "firefox", "path": "${workspaceFolder}/src/ext"
"request": "launch",
"port":6000,
"reAttach": true,
"addonType": "webExtension",
"addonPath": "${workspaceFolder}/dist",
} }
], ]
"firefox": { },
"executable": "/usr/bin/firefox-developer-edition", {
"firefoxArgs": [ "name": "Launch addon (firefox)",
"--start-debugger-server" "type": "firefox",
] "request": "launch",
"port": 6000,
"reAttach": true,
"addonType": "webExtension",
"addonPath": "${workspaceFolder}/dist-ff",
"pathMappings": [
{
"url": "webpack:///ext",
"path": "${workspaceFolder}/src/ext"
},
]
} }
],
"firefox": {
"executable": "/usr/bin/firefox-developer-edition",
"firefoxArgs": [
"--start-debugger-server"
]
},
"chrome": {
"executable": "/usr/bin/google-chrome-stable --remote-debugging-port=9222",
}
} }

86
.vscode/settings.json vendored
View File

@ -1,25 +1,109 @@
{ {
"cSpell.words": [ "cSpell.words": [
"aard",
"allowtransparency",
"ardetector", "ardetector",
"autodetect",
"autodetection", "autodetection",
"beforeunload",
"blackbar", "blackbar",
"blackbars",
"blackframe", "blackframe",
"bruteforce",
"canvas", "canvas",
"clickthrough",
"cmpa",
"cmpb",
"com",
"comms",
"csui",
"ctab",
"cycleable",
"decycle",
"dinked",
"dinks",
"disneyplus",
"endregion",
"equalish", "equalish",
"fith",
"fitw",
"fuckup",
"fucky",
"geoblocked",
"geoblocking",
"gfycat",
"gmail",
"guardline",
"han",
"haram",
"Heebo",
"hoverable",
"iframe",
"imgur",
"insta", "insta",
"jsoneditor",
"jwplayer",
"kavin",
"letterboxed",
"manjaro",
"mdicon",
"mdijs",
"metaivi",
"minification",
"mitigations",
"mstefan",
"nogrow",
"noshrink",
"outro",
"PILLARBOX",
"PILLARBOXED",
"polyfill",
"problemo",
"recursing", "recursing",
"reddit", "reddit",
"rescan",
"resizer", "resizer",
"resizers",
"SCPI",
"scrollbar",
"shitiness",
"smallcaps",
"spaghettified",
"suboption",
"subscan",
"tabitem",
"tablist",
"tamius",
"textbox", "textbox",
"ultrawide",
"ultrawidify",
"unmark",
"unmarking",
"unsets",
"unshift",
"uwid",
"uwui",
"videodata", "videodata",
"youtube" "vids",
"vuejs",
"vuex",
"wakanim",
"webextension",
"webextensions",
"wuckies",
"wucky",
"ycenter",
"youtube",
"YYMM"
], ],
"cSpell.ignoreWords": [ "cSpell.ignoreWords": [
"abcdefghijklmnopqrstuvwxyz",
"autoar", "autoar",
"cheight", "cheight",
"cwidth", "cwidth",
"fcstart", "fcstart",
"fctime", "fctime",
"legacycd",
"ncol", "ncol",
"nrow", "nrow",
"tickrate", "tickrate",

View File

@ -1,18 +1,301 @@
# Changelog # Changelog
## v4.x ## v6.0 (current major)
### Plans for the future ### v6.4.0
* In-player UI now appears in full-screen even for websites that use top layer
* Embedded sites now inherit settings of the parent frame by default
* Setting inheritance/overriding is not thoroughly tested and may be full of edge cases.
* Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.
* Autodetection can now scan for subtitles.
* New experimental autodetection (which is secretly just slightly modified subtitle check)
* Settings page looks ugly af right now. Maybe fix it some time later ### v6.3.0
* rework keyboard shortcuts so things like russian layouts don't mess thigns up * Added zoom segment to in-player UI and popup.
* other bug fixes * Fixed keyboard zoom
* Added additional zoom options. If you wonder how zoom options differ from crop, mess around and find out.
* Subdomains now inherit same settings as their parent domain by default
* Extension popup detects embedded content
* Added `www.youtube-nocookie.com` to "officially supported" list
QoL improvements for me: ### v6.2.5
* logging: allow to enable logging at will and export said logs to a file * Popup appearance changed — UI advertisement panel was moved to the popup header
* Fixed the bug where popup wouldn't be showing the correct settings
* Fixed the bug where site settings would default to 'disabled', even if Extension default setting was not disabled.
* Added ability to export and import settings from file (ft. developer mode editor)
* [dev goodies] Added debug overlay for aard
* Fixed an issue where aspect ratio wouldn't get calculated correctly on youtube videos with native aspect ratios other than 16:9
* Fixed an issue that would crash the extension if video element didn't have a player element associated with it
* Fixed an issue where extension sometimes wouldn't work if video element was grafted/re-parented to a different element
### v4.3.0 (current) ### v6.2.4
* [#264](https://github.com/tamius-han/ultrawidify/issues/264) — fixed issue with white screen that affected some youtube users. Special thanks to [SnowyOwlNugget](https://github.com/SnowyOwlNugget">SnowyOwlNugget), who instead of whining provided the necessary information.
* Minor updates to the settings
* Switching between full screen, theater, and normal player now correctly enables and disables the extension according to the settings.
* Don't reset settings if updating conf patches fails.
* Darken in-player UI backgrounds until Chrome fixes its backdrop-filter bug
### v6.2.3
* Fixed default persistent mode
### v6.2.2
* Fixed the issue where stretching was not applied if no cropping was used
* Added way to manually enable or disable color scheme detection, as a compromise between #259 and #264
### v6.2.1
* Fixed offset issue on Netflix
* Fixed issue with overlay iframe not being transparent on twitter
### v6.2.0
* Parts of automatic aspect ratio detection use WebGL now. This should result in improved performance.
* In-player UI now indicates whether a site is having a problem with automatic aspect ratio detection.
* UI improvements
Since automatic aspect ratio detection had to be rewritten completely and since the new version needs to be out 5 days ago, there are some regressions in the automatic aspect ratio detection.
### v6.1.0
Was skipped due to how major the changes were.
### v6.0.1
* Fixed external links
### v6.0.0
Chrome only, because I needed to rush manifest v3 migration before ensuring things work in Firefox.
* In player UI
* New alignment options (can align video both vertically, as well as horizontally)
* Extension does a little bit of a better job differentiating between levels of support for a given site
* Changed how cropping and panning works. This should lead to fewer problems and better generic support.
* REGRESSION: no manual panning with shift + mouse movement
* "Player select" screen, which provides a GUI way for picking HTML element that acts as the video player area
Regressions and potential issues:
* Settings were largely not migrated from previous versions. This is as intended, since changes to cropping (and some other aspects)
rendered certain old settings obsolete.
* Extension can no longer discriminate between iframes — extension popup commands get sent to ALL iframes on page
* Extension probably can't discriminate between multiple videos on a single page
## v5.x
### v5.1.7
Firefox-only.
* When cropping and panning video, CSS' `transform: translate(x,y)` now uses integers _always_. Before that fix, `translate(x,y)` could be offset by x.5 px, and that half of a pixel introduced a 1px thick line on the portion of the left edge of the video.
### v5.1.7
* When aligning videos, ensure video is never translated by a fractional value
* As of recent nVidia driver update in Edge, Angle detection fails in a way that prevents popup from showing up. This problem should be fixed now.
The angle detection problem was fixed by disabling the angle detection check for now, as Chrome, et al. appear to have fixed buggy video hardware acceleration.
### v5.1.6
* Added new config for disney+ (courtesy of @MStefan99)
* Chrome hardware acceleration is bugged and cannot be worked around in-extension. Only way to fix this is for users to change their Google Chrome settings. Added naggathon with instructions into the extension popup.
### v5.1.5
* Fixed laginess in Chromium-based browsers on Windows. Details in [#199](https://github.com/tamius-han/ultrawidify/issues/199#issuecomment-1221383134)
### v5.1.4
* Fixed some problems with autodetection not returning to 16:9 when necessary if autodetection already changed aspect ratio ([#198](https://github.com/tamius-han/ultrawidify/issues/198))
### v5.1.3
* Fixed some problems with autodetection sometimes briefly resetting on dark frames ([#195](https://github.com/tamius-han/ultrawidify/issues/195), [#196](https://github.com/tamius-han/ultrawidify/issues/196))
### v5.1.2
* `set-extension-mode` turned into `set-ExtensionMode` at some point. This caused in "Enable this extension" options to vanish on certain setups.
* Blackframe tests now run on same data as main algorithm as opposed on a smaller sample (`drawImage()` calls are _very_ expensive even for a 16x9 sample).
### v5.1.1
* Fixed autodetection
### v5.1.0
* Re-enable logger
* Move aspect ratio autodetection to requestAnimationFrame
* Fix netflix
### v5.0.7
* Videos of square-ish aspect ratios on 1440p (and lower) resolutions now no longer get misaligned ([#162](https://github.com/tamius-han/ultrawidify/issues/162))
* Alignment of featured videos on youtube channel page should now also be fixed
### v5.0.6
* Added configuration for metaivi.com based on user feedback ([#160](https://github.com/tamius-han/ultrawidify/issues/160))
* Removed ExtConfPatches for versions < 4.5.0, because nobody should be using a build of this extension that's over a year old
### v5.0.5
* improved UX a bit
* Fixed white background on app.plex.tv ([#158](https://github.com/tamius-han/ultrawidify/issues/158))
### v5.0.4
* Attempt to fix disney+ again, courtesy of [@jwannebo](https://github.com/tamius-han/ultrawidify/issues/84#issuecomment-846334005) on github.
### v5.0.3
* Fixed the issue where the videos were sometimes offset up and left. Again.
* Fix the issue where correcting source stretch was squished incorrectly ([#153](https://github.com/tamius-han/ultrawidify/issues/153))
### v5.0.2
* When in full screen, the extension will assume player element dimensions are the same as the screen resolution. This should help with sites where ultrawidify doesn't correctly identify the player, as cropping generally doesn't work if player element is not identified. Old behaviour can be restored in advanced extension settings by toggling the "use player aspect ratio in fullscreen" checkbox under 'player detection settings'.
* Extension should now respect 'disable extension' option for real.
* Fixed the issue where player wouldn't get detected if video was wider than the player.
### v5.0.1
* Added an option for users to turn off (and/or configure) Chrome/Edge's zoom limiter.
### v5.0.0
There's been some big-ish changes under the hood:
* Migrate main scripts to typescript (vue is currently not included).
* webextension-polyfill is now used everywhere (if only because typescript throws a hissy fit with `browser` and `chrome` otherwise) ([#114](https://github.com/tamius-han/ultrawidify/issues/114))
* Fix some bugs that I didn't even know I had, but typescript kinda shone some light on them
* Manual zoom (Z/U unless sites override the two) should now work again (without automatic AR constantly overriding it). Same goes for panning. ([#135](https://github.com/tamius-han/ultrawidify/issues/135) & [#138](https://github.com/tamius-han/ultrawidify/issues/138))
* Fix issue when video would be scaled incorrectly if video element uses `height:auto`.
* **[5.0.0.1]** Fixed the issue where settings were reset on page load.
* **[5.0.0.1]** Fixed the issue where settings page wouldn't load.
## v4.x (current major)
### v4.5.3
* Provides workaround for the fullscreen stretching bug Chrome 88 (or a recent Windows 10 update) introduced for nVidia users using hardware acceleration on Windows 10. In order to mitigate this bug, Ultrawidify needs to keep a 5-10 px wide black border while watching videos in full screen. This bug is also present in Edge.
* **[4.5.3.1]** Fixed letterbox misalignment binding in settings (#134)
* **[4.5.3.2]** Fixed false 'autodetection not supported' notifications.
### v4.5.2
* Fixed the issue where videos would sometimes get misaligned while using hybrid stretch, except for real this time. ([#125](https://github.com/tamius-han/ultrawidify/issues/125))
* Improved DRM detection (the 'autodetection cannot work on this site' popup should now no longer show up on the sites where autodetection _can_ work)
### v4.5.1
* Fixed the misalignment issue on netflix ... hopefully.
* 'Site settings' tab should now work in Chrome as well ([#126](https://github.com/tamius-han/ultrawidify/issues/126))
* Popup interface now refreshes properly ([#127](https://github.com/tamius-han/ultrawidify/issues/127))
* Videos should now be scaled correctly when the display is narrower than video's native aspect ratio ([#118](https://github.com/tamius-han/ultrawidify/issues/118))
* Fullscreen videos on streamable are aligned correctly ([#116](https://github.com/tamius-han/ultrawidify/issues/118)).
* **[4.5.1.1]** Streamable fix broke old.reddit + RES on embeds from v.redd.it and streamable.com. We're now using an alternative implementation. ([#128](https://github.com/tamius-han/ultrawidify/issues/128))
* **[4.5.1.2]** Fixed the issue where videos would sometimes get misaligned while using hybrid stretch. ([#125](https://github.com/tamius-han/ultrawidify/issues/125))
* **[4.5.1.3]** Added fix for disney plus
* **[4.5.1.3]** Microsoft Edge has fixed the bugs that prevented the extension from working properly. Popup should no longer be shown.
### v4.5.0 (Current)
* Under the hood: migrated from vue2 to vue3, because optional chaining in templates is too OP.
* (On options page, section 'Action &amp; shortcuts') Manual aspect ratio now supports entering custom ratios using '21/9' and '2.39:1' formats (as opposed to single number, e.g. '2.39') — [#121](https://github.com/tamius-han/ultrawidify/issues/121).
* Added config for wakanim.tv (special thanks to @saschanaz for doing the legwork — [#113](https://github.com/tamius-han/ultrawidify/issues/113))
* (In Firefox) When extension was placed in overflow menu, the popup was cut off. That should be fixed now. [#119](https://github.com/tamius-han/ultrawidify/issues/119)
* The extension will now show a notification when autodetection can't run due to DRM
* Videos on facebook and reddit no longer get shifted up and to the left for me (cropping most of the video off-screen), but I haven't been deliberately trying to fix that issue. If you experience that issue, please consider contacting me (via github or email) with a link to a problematic video.
### v4.4.10
* Video alignment should now work on Twitch — [#109](https://github.com/tamius-han/ultrawidify/issues/109)
* Videos should now align properly on Hulu while cropped — [#111](https://github.com/tamius-han/ultrawidify/issues/111) & via email
* Fixed a problem where changing certain settings would cause multiple instances of Ultrawidify to run on a page, effectively preventing some crop options to be set until reload. (possibly [#112](https://github.com/tamius-han/ultrawidify/issues/112)?)
* Fixed a problem where embedded videos would be misaligned after switching from full screen
* **[4.4.10.1]** Fixed cruncyhroll regression — [#109](https://github.com/tamius-han/ultrawidify/issues/115)
### v4.4.9
* Fixed the youtube alignment issue (previously fixed in v4.4.7.1-2), but this time for real (and in a bit more proper way)
* Fixed the bug where extension wouldn't work when URL specified a port (e.g. www.example.com:80)
* **[4.4.9.1]** removed source files from extension build in order to decrease package size
* **[4.4.9.2]** updated dependencies and stuff
In addition to that, as of 4.4.9.1 the build process ensures removal of `node_modules` before building the extension so we can have reproducible builds except for real this time. Hopefully.
### v4.4.8
* Fixed the bug where on pages with more than one video, the list of available videos in the extension popup wouldn't remove videos that are no longer displayed on site. This resulted in extension listing videos that were no longer on the page. Reboot or navigation would also not clear the list if navigating between various pages on the same host.
* Fixed the chrome-only bug where on sites with more than one video, the number wouldn't get hidden when the extension popup closed.
### v4.4.7
* Removed unnecessary font files and image files from the package.
* LoggerUI is now functional.
* **[4.4.7.1]** Additional CSS fixes
* **[4.4.7.1]** Bruteforce fix for youtube alignment issue
* **[4.4.7.2]** Bruteforce fix for youtube alignment issue — increase number of retries
### v4.4.6
* Ensured that Vue part of the content script (logger UI) only loads when necessary in order to fix breakage on certain sites (#96).
* Disabling (or enabling, if running in whitelist-only mode) specific sites used to not work (#91). This issue appears to have been fixed.
* Default stretch mode for sites is now probably being observed, too (#94).
* Fixed netflix (and possibly disney+ )
* It's been almost a month and Chrome Web Store still hasn't finished the review of the 4.4.4.1 (and 4.4.4.2) revisions because when it comes to incompetence, it's hard to expect anything less from Google. I've did some proverbial yelling at the support in hopes that Chrome version will finally see an update (disclaimer: when I said yelling I really mean a polite request, because support staff doesn't deserve abuse because a different department is utter shite at doing their jobs).
### v4.4.5
* Extension no longer requires `allTabs` and `webNavigation` permissions
* Some CSS on the debugger popup was not scoped, causing issues with some sites.
* Fix some additional issues with video alignment when changing video on autoplay
### v4.4.4
* Tab detection in extension popup has been made more accurate
* QoL: Added user-accessible logger (to make fixing sites I can't access a bit easier)
* Changed links to reflect my github username change
* **[4.4.4.1]** Fix broken extension popup.
* **[4.4.4.1]** Fix global/site settings not getting applied immediately/only getting applied after page reload.
* **[4.4.4.2]** Fix problem with video being offset while switching between full screen and non-fullscreen non-theater mode on Youtube
### v4.4.3
* Fixed conf patch for disney+ (hopefully) (v4.4.3.1: but for real)
* `Settings.save()` adds missing values to site config when saving extension configuration.
### v4.4.2
* New stretching modes that squish video to a specified aspect ratio. One of the two modes correct aspect ratio before cropping, the other corrects after cropping.
* Potential fix for disney+ — added an option that forces extension to force player re-detection.
* Fixed bug where certain custom actions that should be removable weren't removable
### v4.4.1
* Changes to player detection that fix issues with vk
* Extension tries to avoid setting aspect ratio pointlessly
* (Hopefully) fixed mailto: and reddit compose links.
* When reporting bugs, email/reddit template now automatically gathers browser, extension version and OS.
### v4.4.0
* Russian users (and users of other non-latin keyboard layouts) can now use keyboard shortcuts by default, without having to rebind them manually. (NOTE: if you've changed keyboard shortcuts manually, this change will ***NOT*** be applied to your configuration.)
* NOTE: when using non-latin layouts, 'zoom' shortcut (`z` by default) uses the position of 'Y' on QWERTY layout.
* Ability to preserve aspect ratio between different videos (applies to current page and doesn't survive proper page reloads)
* Changing aspect ratio now resets zooming and panning.
* Fixed bug where keyboard shortcuts would work while typing in certain text fields
* Fixed minor bug with autodetection
* **[4.4.0.1]** fixed mailto and reddit compose links. When reporting issues via e-mail or reddit, extension version, browser
and OS are automatically included in email/reddit template.
### v4.3.1
* Minor rework of settings page (actions & shortcuts section)
* Fixed bug that prevented settings page from opening
* **[4.3.1.1]** quick patch for twitch.tv
### v4.3.0
* Fixed some issues with incorrect alignment after window resize * Fixed some issues with incorrect alignment after window resize
* Fixed all sorts of issues for videos hosted on v.reddit for new (and old) reddit * Fixed all sorts of issues for videos hosted on v.reddit for new (and old) reddit

39
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,39 @@
# Contributing to Ultrawidify
Thank you for considering contributing to this project! We welcome contributions from the community and are grateful for your efforts.
## How to Contribute
0. Open an issue, where you introduce the feature you want to add or thing you want to fix
1. Fork the repository.
2. Create a new branch for your contribution.
3. Write readable code
4. Check that your additions do not break existing features
5. Open a pull request
## Code of Conduct
Please follow the code of conduct:
* don't be too much of an asshole
* language policing my code comments is not a worthwhile contribution, and is considered spam. If you're here to do that, fuck off. I'm looking at you, ScamOSS and co.
## Code Style
- Use formatting consistent with the rest of the project
- Write meaningful commit messages.
- Include comments where necessary to explain complex logic.
## Licensing & Contribution Agreement
By submitting a contribution (e.g., via pull request), you agree to the following:
- You grant the project maintainer(s) a non-exclusive, irrevocable, worldwide, royalty-free license to use, copy, modify, and distribute your contributions as part of the project.
- You certify that your contributions are your original work and that you have the right to submit them.
- You agree that your contributions are licensed under the same license as the rest of the project, unless explicitly stated otherwise.
**TL;DR:** once your code is in the project, there's no take-backsies.
## Questions?
The discussion board is right over there, three tabs to the right.

6
DOCUMENTATION.MD Normal file
View File

@ -0,0 +1,6 @@
# Implementation details
## Enabling/disabling aspect ratio corrections
* Aspect ratios are changed by proxy. Extension attaches **a custom CSS class** to `video` and `player` elements.
* To prevent extension from affecting the appearance of a webpage, **it's sufficient to remove our custom CSS classes from `video` and `player` elements.**

32
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,32 @@
// required jenkins plugins:
// * https://plugins.jenkins.io/git/
pipeline {
agent any
stages {
// stage('Check for changes') {
// sh "env.GIT_COMMIT != env.GIT_PREVIOUS_COMMIT"
// }
stage('Install dependencies') {
steps {
sh 'npm ci'
}
}
stage('Build') {
steps {
sh 'npm run build-all'
}
}
stage('Push to release server') {
steps {
sh "echo 'implement me pls!'"
}
}
}
}

35
LICENSE.MD Normal file
View File

@ -0,0 +1,35 @@
# License
Copyright (c) 2025 @tamius-han
The source code is provided on the "you can look, but you can't take" basis.
## Permissions
You are permitted to:
* You can view the code
* You can create forks and modify the code for personal use
* You can build and run modified versions of this project for personal use on your local machine
* Push modifications to your personal github fork
## Restrictions
You may NOT:
* Distribute this project or any modified versions outside of your personal Github fork
* Publish or distribute compiled binaries, builds, or packages of this project or any derivative work
* Use the code in commercial products, services, or other public-facing deployments.
* Sub-license, sell, or otherwise make the software or derivatives available to third parties.
## Contributions
By submitting a pull request or other contribution, you agree that:
- You grant the project maintainer(s) a non-exclusive, irrevocable, worldwide, royalty-free license to use, copy, modify, and distribute your contributions as part of the project.
- You certify that your contributions are your original work and that you have the right to submit them.
- You agree that your contributions are licensed under the same license as the rest of the project, unless explicitly stated otherwise.
## Disclaimer
This software is provided "as is", without warranty of any kind, express or implied. Use at your own risk.

View File

@ -0,0 +1,23 @@
Microsoft edge extension store requires notes for certification. Because I'm too lazy to whip this text over and over and over and over and over and over again, I'm making it a copypasta.
---------------------------------------------------------------------------------------------
This extension is targeted at users of 21:9 monitors. If using 16:9 monitor, use youtube in THEATER MODE.
Not all videos need to have their aspect ratio corrected. Here is few examples of videos with problems this extension is trying to solve:
* Variable aspect ratio — autodetection testing grounds: https://www.youtube.com/watch?v=-IHzprfs6tw
* Static (but incorrect) aspect ratio: https://www.youtube.com/watch?v=BTETsm79D3A
Twitch example (collapse subscription & chat sidebars on 16:9, do not fullscreen) — aspect ratio switch happens at around 1:50:00 mark:
* https://www.twitch.tv/videos/330639009?t=1h49m45s
Autodetect has technological limitations and can incorrectly detect aspect ratio. This is acceptable — user should override it in such cases.
Functions labelled 'experimental' are experimental and may not function correctly.
Websites may interfere with keyboard shortcuts.
Brief video flicker when changing between fullscreen, non-fullscreen or when changing browser is inherent artifact caused by sites not being aware of extension and is thus considered acceptable.
Logger option does nothing (except give you a popup) unless you have a logger config handy. It is designed for debugging in case of bug reports.

View File

@ -4,14 +4,28 @@
The extension is built on a PC running Manjaro Linux. npm and node are installed from repositories/aur. The extension is built on a PC running Manjaro Linux. npm and node are installed from repositories/aur.
### Software versions:
## Installing dependencies Node/npm versions:
Run `npm ci` ```
node: %%NODE_VERSION%%
npm: %%NPM_VERSION%%
```
Linux (`uname -a`):
```
%%LINUX_VERSION%%
```
## Reproducing build ## Reproducing build
`npm run build` Run the following commands to install dependencies and compile the firefox build:
The compiled code pops up in /dist. ```
npm ci
npm run build
```
The compiled code pops up in `/dist-ff`.

View File

@ -14,4 +14,4 @@ todo
#This extension asks for permission that isn't listed above. #This extension asks for permission that isn't listed above.
Sometimes (and by 'sometimes' I mean 'way too often') I forget to update README files. If there's a permission that I haven't wrote an explanation for, please [open an issue](https://github.com/xternal7/ultrawidify/issues). Sometimes (and by 'sometimes' I mean 'way too often') I forget to update README files. If there's a permission that I haven't wrote an explanation for, please [open an issue](https://github.com/tamius-han/ultrawidify/issues).

View File

@ -1,8 +1,10 @@
# Ultrawidify — aspect ratio fixer for youtube and netflix # Ultrawidify — aspect ratio fixer for youtube and netflix
## Super TL;DR: I'm just looking for the install links, thanks ## Super TL;DR: I'm just looking for the install links, thanks
[Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/), [Chrome](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi), [Edge](#edge-speficic-limitations-important). [Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/), [Chrome](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi), [Edge](https://microsoftedge.microsoft.com/addons/detail/ultrawidify/lmpgpgechmkkkehkihpiddbcbgibokbi).
There's also [nightly "builds"](https://stuff.lionsarch.tamius.net/ultrawidify/nightly/).
## TL;DR ## TL;DR
@ -11,11 +13,10 @@ If you own an ultrawide monitor, you have probably noticed that sometimes videos
![Demo](img-demo/example-httyd2.png "Should these black bars be here? No [...] But an ultrawide user never forgets.") ![Demo](img-demo/example-httyd2.png "Should these black bars be here? No [...] But an ultrawide user never forgets.")
## Known issues ## Known issues
* Netflix autodetection not working in Chrome, wontfix as issue is fundamentally unfixable. * Netflix autodetection not working in Chrome and working poorly in Firefox. This problem happens because DRM, and happens on other sites utilizing DRM protection schemes. Don't expect Chrome support any time soon.
* Everything reported in [issues](https://github.com/xternal7/ultrawidify/issues) * Everything reported in [issues](https://github.com/tamius-han/ultrawidify/issues)
### Limitations ### Limitations
@ -23,15 +24,14 @@ If you own an ultrawide monitor, you have probably noticed that sometimes videos
* Autodetection is only correct 95% of the time, most of the time. * Autodetection is only correct 95% of the time, most of the time.
* That new stretching mode wasn't thoroughly tested yet. Issues may be present. (Same with zoom) * That new stretching mode wasn't thoroughly tested yet. Issues may be present. (Same with zoom)
* Enabling extension everywhere (as opposed to whitelisted sites) could break some websites. * Enabling extension everywhere (as opposed to whitelisted sites) could break some websites.
* Edge has
### Features ### Features
* **Can be enabled or disabled on per-site basis** * **Can be enabled or disabled on per-site basis**
* **Crop video to fit screen** (no stretching. Supported aspect ratios: 21/9 (1:2.39), 16:9, 16:10, _one (1) custom aspect ratio_) * **Crop video to fit screen** (no stretching. Supported aspect ratios: 21/9 (1:2.39), 16:9, 16:10. It's possible to set additional aspect ratios, but settings GUI currently contains some mildly annoying bugs)
* **Automatic aspect ratio detection** (can be enabled/disabled entirely or on a per-site basis, separately of the extension. Autodetection in action: [youtube](https://www.youtube.com/watch?v=j2xn1WpbtCQ)) * **Automatic aspect ratio detection** (can be enabled/disabled entirely or on a per-site basis, separately of the extension. May not work on sites utilizing DRM schemes, such as Netflix et. al.). Autodetection in action: [youtube](https://www.youtube.com/watch?v=j2xn1WpbtCQ))
* **Supports Youtube theater mode** * **Supports Youtube theater mode**
* **[EXPERIMENTAL!]** Stretch video to fit the screen (4 different approaches) * **[EXPERIMENTAL!]** Stretch video to fit the screen
* **[EXPERIMENTAL!]** custom zooming and panning * **[EXPERIMENTAL!]** custom zooming and panning
@ -39,19 +39,30 @@ If you own an ultrawide monitor, you have probably noticed that sometimes videos
* Youtube * Youtube
* Netflix * Netflix
* Twitch
### Other sites ### Other sites
I am not actively testing extension on other sites. You can try your luck and enable extension for any unsupported site you stumble across via extension popup, but I make no guarantees it will work everywhere. I am not actively testing extension on other sites. You can try your luck and enable extension for any unsupported site you stumble across via extension popup, but I make no guarantees it will work everywhere.
If extension doesn't work for a site I'm not testing on out of the box, follow [this wiki](https://github.com/tamius-han/ultrawidify/wiki/Fixing-site-incompatibilites-('Advanced-settings')). The 'quick and dirty' approach should work for most sites. (If you try doing things the proper way, you should really know what you're doing.)
### Installing this extension ### Installing this extension
You can download this extension from Firefox' and Chrome's extension stores: You can download this extension from the relevant extension stores:
* [Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/) * [Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/)
* [Chrome](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi) * [Chrome](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi)
* [Edge](https://microsoftedge.microsoft.com/addons/detail/ultrawidify/lmpgpgechmkkkehkihpiddbcbgibokbi)
This extension also has a version for Microsoft Edge, but that requires a bit more effort. [See notes first](#edge-speficic-limitations-important). Other browsers are not officially supported. If you're using a different Chromium-based browser, you can try installing the addon from the Chrome Web Store — but if things don't work, you're on your own.
### Nightly builds
* Nightly builds can be downloaded [here](https://stuff.lionsarch.tamius.net/ultrawidify/) as an unpacked extension that can
only be installed temporarily.
If I did anything during the day, the nightly version will be sorta-built at whatever my VPS provider thinks is 4AM CE(S)T.
# Beggathon (donations) # Beggathon (donations)
@ -71,7 +82,7 @@ You can make a donation [via Paypal](https://www.paypal.me/tamius).
# The long version # The long version
The technology has been here for a while, but plenty of people don't know how to properly encode a video (despite the fact [youtube has an article that explains aspect ratios](https://support.google.com/youtube/answer/6375112)). Plenty of people surprisingly includes major Holywood studios, such as [Marvel](https://www.youtube.com/watch?v=Ke1Y3P9D0Bc), [Disney](https://www.youtube.com/watch?v=yCOPJi0Urq4), [Dreamworks](https://www.youtube.com/watch?v=oKiYuIsPxYk), [Warner Brothers](https://www.youtube.com/watch?v=VYZ3U1inHA4), [Sony](https://www.youtube.com/watch?v=7BWWWQzTpNU), et cetera. You'd think that this is the one thing Holywood studios and people who make [music videos for a living](https://www.youtube.com/watch?v=c6Mx2mxpaCY) would know how to do right, but they don't. This extension is here to fix that. The technology has been here for a while, but plenty of people don't know how to properly encode a video (despite the fact [youtube has an article that explains aspect ratios](https://support.google.com/youtube/answer/6375112)). Plenty of people surprisingly includes major Hollywood studios, such as [Marvel](https://www.youtube.com/watch?v=Ke1Y3P9D0Bc), [Disney](https://www.youtube.com/watch?v=yCOPJi0Urq4), [Dreamworks](https://www.youtube.com/watch?v=oKiYuIsPxYk), [Warner Brothers](https://www.youtube.com/watch?v=VYZ3U1inHA4), [Sony](https://www.youtube.com/watch?v=7BWWWQzTpNU), et cetera. You'd think that this is the one thing Hollywood studios and people who make [music videos for a living](https://www.youtube.com/watch?v=c6Mx2mxpaCY) would know how to do right, but they don't. This extension is here to fix that.
![Jesus Christ.](img-demo/example-jasonbourne.png "This is indeed worse than Snowden.") ![Jesus Christ.](img-demo/example-jasonbourne.png "This is indeed worse than Snowden.")
@ -218,13 +229,12 @@ The keyboard shortcuts have already been listed, but let's list them all again i
### Rebinding keyboard shortcuts ### Rebinding keyboard shortcuts
is currently not possible. Settings page for this extension has been disabled sometime with 2.0 release (because it [broke](https://github.com/xternal7/ultrawidify/issues/16)), and fixing the setting page has been very low priority as I've had more important issues to work on. is currently not possible. Settings page for this extension has been disabled sometime with 2.0 release (because it [broke](https://github.com/tamius-han/ultrawidify/issues/16)), and fixing the setting page has been very low priority as I've had more important issues to work on.
However, I do plan on implementing this feature. Hopefully by the end of the year, but given how consistently I've been breaking self-imposed deadlines and goals for this extension don't hold your breath. After all, [Hofstadter's a bitch](https://en.wikipedia.org/wiki/Hofstadter%27s_law). However, I do plan on implementing this feature. Hopefully by the end of the year, but given how consistently I've been breaking self-imposed deadlines and goals for this extension don't hold your breath. After all, [Hofstadter's a bitch](https://en.wikipedia.org/wiki/Hofstadter%27s_law).
## Plans for the future ## Plans for the future
1. Handle porting of extension settings between versions. 1. Handle porting of extension settings between versions.
@ -243,13 +253,22 @@ However, I do plan on implementing this feature. Hopefully by the end of the yea
[Latest stable for Chrome — download from Chrome store](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi) [Latest stable for Chrome — download from Chrome store](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi)
Edge version is currently not available, as Edge has some bugs that prevent this extension from working correctly. [Read more](https://github.com/tamius-han/ultrawidify/issues/117#issuecomment-747109695)
<!-- [Latest stable for Edge — Download from Microsoft store](https://microsoftedge.microsoft.com/addons/detail/lmpgpgechmkkkehkihpiddbcbgibokbi) -->
### Installing the current, github version ### Installing the current, github version
## Get pre-built version:
TODO
## Build from source
Requirements: npm, node. Requirements: npm, node.
1. Clone this repo 1. Clone this repo
2. run `npm install` 2. run `npm install`
3. If using **Firefox,** run: `npm run watch:dev`. If using **Chrome,** run: `npm run watch-chrome:dev`. If using Edge, run: `npm run watch-edge:dev`. 3. If using **Firefox,** run: `npm run watch:dev`. If using **Chrome,** run: `npm run watch-chrome:dev`.
TODO: see if #3 already loads the extension in FF TODO: see if #3 already loads the extension in FF
@ -258,28 +277,6 @@ TODO: see if #3 already loads the extension in FF
4. Add temporary addon 4. Add temporary addon
5. Select `${ultrawidify_folder}/dist/manifest.json` 5. Select `${ultrawidify_folder}/dist/manifest.json`
# Edge-specific limitations (IMPORTANT!)
For various reasons — most notably, I refuse to pay Microsoft €14 for the privilege of developing shit for their outright broken browser (and in addition to that, the extension needs to go through review process as well) — this extension isn't going to appear on Microsoft Store. (And I do not permit anyone else to do that in my name either). Full rant on why I've made this decision can be found [here](https://github.com/xternal7/ultrawidify/issues/14#issuecomment-424903335).
As a result, you'll have to download the extension and install it manually. This approach has some downsides.
1. You'll get this popup after starting Edge. If you've already opened Youtube or Netflix, **you will have to reload the page (or navigate to somewhere else) in order for extension to start.**
![slika](https://user-images.githubusercontent.com/12505802/46114175-05912200-c1e1-11e8-91c7-2217f5bf79e3.png)
2. Certain WebExtension APIs that I rely on are outright broken in Edge. This bug would cause global extension settings (tab: Extension settings) and per-site settings (tab: Site settings) to reset to default values every time you'd open the popup. As a result, _Extension settings_ and _Site settings_ tabs are disabled in Edge:
![Feast on dem popup](https://user-images.githubusercontent.com/12505802/46113923-d1693180-c1df-11e8-82f0-ad64cbc57558.png)
Unfortunate consequence of this is that you won't be able to enable this extension for sites other than Youtube and Netflix, but then again. Let's be honest. You're only using Edge for Netflix, so that's probably no big deal for you.
**It's also worth noting that I'm not actively maintaining the Edge fork, so it's a few versions behind.**
## Installing Ultrawidify in M$ Edge
1. Download the zip file from [here](https://github.com/xternal7/ultrawidify/tree/master/releases/edge)
2. Extract contents of the zip file in some folder. It really doesn't matter where, just keep it somewhere that won't be in your way when using your computer.
3. Follow the steps in [this guide](https://docs.microsoft.com/en-us/microsoft-edge/extensions/guides/adding-and-removing-extensions)
# Changelog # Changelog
see changelog.md see changelog.md

28770
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,88 @@
{ {
"name": "ultravidify", "name": "ultrawidify",
"version": "4.3.0", "version": "6.3.998",
"description": "Aspect ratio fixer for youtube that works around some people's disability to properly encode 21:9 (and sometimes, 16:9) videos.", "description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
"author": "Tamius Han <tamius.han@gmail.com>", "author": "Tamius Han <tamius.han@gmail.com>",
"scripts": { "scripts": {
"start": "cross-env HMR=true npm run build:dev -- --watch", "build": "npm run pre-build; cross-env NODE_ENV=production BROWSER=firefox CHANNEL=stable webpack",
"build": "cross-env NODE_ENV=production BROWSER=firefox webpack --hide-modules", "build-all": "bash ./scripts/build-all.sh",
"build-chrome": "cross-env NODE_ENV=production BROWSER=chrome webpack --hide-modules", "build-chrome": "cross-env NODE_ENV=production BROWSER=chrome CHANNEL=stable webpack",
"build-edge": "cross-env NODE_ENV=production BROWSER=edge webpack --hide-modules", "build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack",
"build:dev": "cross-env NODE_ENV=development BROWSER=firefox webpack --hide-modules", "build-edge": "cross-env NODE_ENV=production BROWSER=edge CHANNEL=stable webpack",
"build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack --hide-modules", "build-nightly": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=nightly webpack",
"build-edge:dev": "cross-env NODE_ENV=development BROWSER=edge webpack --hide-modules", "build-nightly-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=nightly webpack",
"build-all": "rm ./dist-zip/uw-amo-source.zip; mv ./dist-zip/*.zip ./dist-zip/old/; npm run build; node scripts/build-zip.js ff; npm run build-chrome; node scripts/build-zip.js chrome; ./scripts/prepare-amo-source.sh", "build-testing": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=testing webpack",
"build-testing-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=testing webpack",
"build-zip": "node scripts/build-zip.js", "build-zip": "node scripts/build-zip.js",
"watch": "npm run build -- --watch", "build:dev": "webpack",
"watch-chrome": "npm run build-chrome -- --watch", "dev": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- --watch\" \"cross-env BROWSER=chrome npm run build:dev -- --watch\" \"cross-env BROWSER=edge npm run build:dev -- --watch\"",
"watch-edge": "npm run build-edge -- --watch", "dev:windows": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- -w --watch-poll\" \"cross-env BROWSER=chrome npm run build:dev -- -w --watch-poll\" \"cross-env BROWSER=edge npm run build:dev -- -w --watch-poll\"",
"watch:dev": "cross-env HMR=true npm run build:dev -- --watch", "dev-ff": "rm -rf ./dist-ff && cross-env NODE_ENV=development CHANNEL=dev BROWSER=firefox npm run build:dev -- --watch",
"watch-chrome:dev": "cross-env HMR=true npm run build-chrome:dev -- --watch", "dev-chrome": "rm -rf ./dist-chrome && cross-env NODE_ENV=development CHANNEL=dev BROWSER=chrome npm run build:dev -- --watch",
"watch-edge:dev": "cross-env HMR=true npm run build-edge:dev -- --watch" "dev-edge": "rm -rf ./dist-edge && cross-env NODE_ENV=development CHANNEL=dev BROWSER=edge npm run build:dev -- --watch",
"pre-build": "rm -rf ./dist-ff; rm -rf ./dist-chrome; rm -rf ./dist-edge; rm -rf ./node_modules; npm ci",
"start": "npm run dev",
"start:windows": "npm run dev:windows"
}, },
"dependencies": { "dependencies": {
"@types/core-js": "^2.5.0", "@babel/plugin-proposal-class-properties": "^7.18.6",
"@types/es6-promise": "^3.3.0", "@mdi/font": "^7.4.47",
"fs-extra": "^7.0.1", "@mdi/js": "^7.4.47",
"vue": "^2.5.17", "@types/resize-observer-browser": "^0.1.11",
"vuex": "^3.0.1", "concurrently": "^9.2.1",
"vuex-webextensions": "^1.2.6" "fs-extra": "^11.3.2",
"gl-matrix": "^3.4.4",
"json-cyclic": "1.0.2",
"json-difference": "^1.16.1",
"lodash": "^4.17.21",
"mdi-vue": "^3.0.13",
"typescript": "^5.9.3",
"vanilla-jsoneditor": "^3.10.0",
"vue": "^3.5.25",
"vue-style-loader": "^4.1.3",
"vuex": "^4.1.0",
"vuex-webextensions": "^1.3.3",
"webextension-polyfill": "^0.12.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.1.2", "@babel/core": "^7.28.5",
"@babel/plugin-proposal-optional-chaining": "^7.0.0", "@babel/preset-env": "^7.28.5",
"@babel/preset-env": "^7.1.0", "@tailwindcss/postcss": "^4.1.17",
"archiver": "^3.0.0", "@types/chrome": "0.1.31",
"babel-loader": "^8.0.2", "@types/core-js": "^2.5.8",
"copy-webpack-plugin": "^4.5.3", "@types/es6-promise": "^3.3.2",
"cross-env": "^5.2.0", "@types/firefox": "0.0.34",
"css-loader": "^0.28.11", "@types/lodash": "^4.17.21",
"ejs": "^2.6.1", "@types/node": "^24.10.1",
"file-loader": "^1.1.11", "@vue/cli": "^5.0.9",
"mini-css-extract-plugin": "^0.4.4", "@vue/cli-plugin-typescript": "^5.0.9",
"node-sass": "^4.9.3", "@vue/compiler-sfc": "^3.5.25",
"sass-loader": "^7.1.0", "archiver": "^7.0.1",
"vue-loader": "^15.4.2", "babel-loader": "^10.0.0",
"vue-template-compiler": "^2.5.17", "babel-preset-es2020": "^1.0.2",
"web-ext-types": "^2.1.0", "copy-webpack-plugin": "^13.0.1",
"webpack": "^4.20.2", "cross-env": "^10.1.0",
"webpack-chrome-extension-reloader": "^0.8.3", "css-loader": "^7.1.2",
"webpack-cli": "^3.1.2", "ejs": "^3.1.10",
"webpack-shell-plugin": "^0.5.0" "file-loader": "^6.2.0",
"mini-css-extract-plugin": "^2.9.4",
"postcss": "^8.5.6",
"postcss-import": "^16.1.1",
"postcss-nested": "^7.0.2",
"postcss-preset-env": "^10.4.0",
"postcss-strip-inline-comments": "^0.1.5",
"raw-loader": "^4.0.2",
"resolve-url-loader": "^5.0.0",
"tailwindcss": "^4.1.17",
"ts-loader": "^9.5.4",
"tsconfig-paths": "^4.2.0",
"vue-cli-plugin-vue-next": "~0.1.4",
"vue-loader": "^17.4.2",
"vue-tsc": "^3.1.5",
"web-ext-types": "^3.2.1",
"webpack": "^5.103.0",
"webpack-cli": "^6.0.1",
"webpack-shell-plugin": "^0.5.0",
"webpack-shell-plugin-next": "^2.3.3"
} }
} }

10
paths.config.js Normal file
View File

@ -0,0 +1,10 @@
const path = require('path')
module.exports = {
'@src': path.resolve(__dirname, 'src'),
'@ui': path.resolve(__dirname, 'src/ui'),
'@components': path.resolve(__dirname, 'src/ui/components'),
'@res': path.resolve(__dirname, 'src/ui/res'),
'@': path.resolve(__dirname, 'src'),
};

24
postcss.config.js Normal file
View File

@ -0,0 +1,24 @@
const path = require('path');
const aliases = require('./paths.config');
module.exports = {
plugins: [
require('postcss-import')({
resolve(id) {
for (const alias in aliases) {
if (id === alias || id.startsWith(alias + '/')) {
return path.resolve(
aliases[alias],
id.slice(alias.length + 1)
)
}
}
return id
},
}),
require('@tailwindcss/postcss'),
require('postcss-nested'),
require('autoprefixer'),
],
};

58
scripts/build-all.sh Normal file
View File

@ -0,0 +1,58 @@
#!/bin/bash
# NOTE: this script needs to be run with the npm run build-all
# command from the root directory of the project. Running it in
# any other way probably isn't going to work.
# pre-build steps:
echo "—————————————————————————————————————————————————————";
echo " BUILDING ALL ULTRAWIDIFY VERSIONS";
echo "—————————————————————————————————————————————————————";
mkdir -p ./build/old
npm run pre-build
echo "::"
echo "::"
echo "::"
echo ":: Pre-build complete. Moving old builds from dist-zip ..."
rm ./dist-zip/uw-amo-source.zip
mv -f ./dist-zip/*.zip ./build/old
echo "::"
echo "::"
echo "::"
echo ":: dist-zip is clean. Starting builds ..."
# lets force raise ram limit, but the improper way
# export NODE_OPTIONS=--max_old_space_size=4096
# build the version for each browser and create a zip afterwards
# step 1: define build functions
#function buildFF {
npm run build
node scripts/build-zip.js ff
#}
#function buildChrome {
npm run build-chrome
node scripts/build-zip.js chrome
#}
#function buildEdge {
npm run build-edge
node scripts/build-zip.js edge
#}
# step 2: execute them all at once
# buildFF &
# buildChrome &
# buildEdge &
# wait < <(jobs -p)
# prepare AMO source
# source code needs to be prepared AFTER
# the code has been built, to ensure that
# package-lock.json remains unchanged
./scripts/prepare-amo-source.sh

94
scripts/build-and-deploy.sh Executable file
View File

@ -0,0 +1,94 @@
#!/bin/bash
# env variables to set:
#
# FORCE_BUILD set this variable to "true" if you wanna force build, even if build was
# triggered without any commits pushed to the directory
#
# BUILD_SCRIPT build-testing or build-nightly, could be any other from package.json tho
#
# RELEASE_SERVER target server (where to push built zips after they've been built)
# RELEASE_DIRECTORY base directory on the target server
# BUILD_CHANNEL_DIRECTORY directory for uploads, inside the release directory
#
# AMO_API_KEY needed if you want to sign and push extension to addons.mozilla.org
# AMO_API_SECRET -||-
echo "============= STARTING BUILD SCRIPT ============="
pwd
whoami
echo " ::: env dump"
echo " -> FORCE_BUILD: $FORCE_BUILD"
echo " -> BUILD_SCRIPT: $BUILD_SCRIPT"
echo " -> RELEASE_SERVER: $RELEASE_SERVER"
echo " -> RELEASE_DIRECTORY: $RELEASE_DIRECTORY"
echo " -> BUILD_CHANNEL_DIRECTORY: $BUILD_CHANNEL_DIRECTORY"
# don't build if nothing has changed, unless overriden via env variable
if [ ! -z "$GIT_COMMIT" ] ; then
if [ ! -z "$GIT_PREVIOUS_COMMIT" ] ; then
if [ "$GIT_COMMIT" == "$GIT_PREVIOUS_COMMIT" ] ; then
if [ $FORCE_BUILD == false ] ; then
echo "--------------------------------------------"
echo " Nothing has changed. Aborting build."
echo "--------------------------------------------"
exit 0;
fi
fi
fi
fi
# let's raise RAM limit for npm command globally
NODE_OPTIONS=--max_old_space_size=4096
npm ci
rm -rf ./dist-zip || true # no big deal if ./dist-zip doesn't exist
mkdir dist-zip # create it back
#
# build firefox
#
npm run "${BUILD_SCRIPT}"
node --max-old-space-size=2048 scripts/build-zip.js ff nightly
# if [ ! -z "${AMO_API_KEY}" ] ; then
# if [ ! -z "${AMO_API_SECRET}" ] ; then
# web-ext sign --source-dir ./dist --api-key "${AMO_API_KEY}" --api-secret "${AMO_API_SECRET}"
# fi
# fi
#
# build chrome
#
npm run "${BUILD_SCRIPT}-chrome"
node --max-old-space-size=2048 scripts/build-zip.js chrome nightly
#
#./scripts/build-crx.sh
#
#
# build edge
#
npm run "${BUILD_SCRIPT}-edge"
node --max-old-space-size=2048 scripts/build-zip.js chrome nightly
######################################
# UPLOAD TO WEB SERVER
######################################
echo "--------------------------------------------"
echo " files ready for upload"
echo "--------------------------------------------"
echo ""
echo "Uploading to server ..."
# push all built stuff to the server
scp -i ~/.ssh/id_rsa -r ./dist-zip/* "ultrawidify-uploader@${RELEASE_SERVER}:${RELEASE_DIRECTORY}${BUILD_CHANNEL_DIRECTORY}"
######################################
# Build finished message
######################################
echo ""
echo "--------------------------------------------"
echo " BUILD FINISHED SUCCESSFULLY"
echo "--------------------------------------------"

45
scripts/build-crx.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/bash -e
#
# Purpose: Pack a Chromium extension directory into crx format
# gotten from: https://web.archive.org/web/20180114090616/https://developer.chrome.com/extensions/crx#scripts
#
# Script assumes we're in basedir of the repository and that extension has been built and zipped to /dist-zip
# using the crx name we want (same name as zip, except different extension)
#
# also this doesn't check for errors ever so
#
echo "entering dist-zip"
cd ./dist-zip
zip=`ls | grep chrome.zip`
name="${zip%.*}"
crx="$name.crx"
key="../keys/chrome-nightly.pem" # this is also good for testing build
pub="$name.pub"
sig="$name.sig"
trap 'rm -f "$pub" "$sig" "$zip"' EXIT
# signature
openssl sha1 -sha1 -binary -sign "$key" < "$zip" > "$sig"
# public key
openssl rsa -pubout -outform DER < "$key" > "$pub" 2>/dev/null
byte_swap () {
# Take "abcdefgh" and return it as "ghefcdab"
echo "${1:6:2}${1:4:2}${1:2:2}${1:0:2}"
}
crmagic_hex="4372 3234" # Cr24
version_hex="0200 0000" # 2
pub_len_hex=$(byte_swap $(printf '%08x\n' $(ls -l "$pub" | awk '{print $5}')))
sig_len_hex=$(byte_swap $(printf '%08x\n' $(ls -l "$sig" | awk '{print $5}')))
(
echo "$crmagic_hex $version_hex $pub_len_hex $sig_len_hex" | xxd -r -p
cat "$pub" "$sig" "$zip"
) > "$crx"
echo "Wrote $crx"
echo "exiting dist-zip"
cd ..

View File

@ -4,12 +4,8 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const archiver = require('archiver'); const archiver = require('archiver');
const DEST_DIR = path.join(__dirname, '../dist'); const extractExtensionData = (browserPostfix) => {
const DEST_ZIP_DIR = path.join(__dirname, '../dist-zip'); const extPackageJson = require(`../dist-${browserPostfix}/manifest.json`);
const extractExtensionData = () => {
const extPackageJson = require('../src/manifest.json');
return { return {
name: extPackageJson.name, name: extPackageJson.name,
@ -17,9 +13,9 @@ const extractExtensionData = () => {
} }
}; };
const makeDestZipDirIfNotExists = () => { const makeDirIfNotExists = (dir) => {
if(!fs.existsSync(DEST_ZIP_DIR)) { if(!fs.existsSync(dir)) {
fs.mkdirSync(DEST_ZIP_DIR); fs.mkdirSync(dir);
} }
} }
@ -42,13 +38,37 @@ const buildZip = (src, dist, zipFilename) => {
const main = () => { const main = () => {
const browser = process.argv[2]; const browser = process.argv[2];
const testingOrNightly = process.argv[3];
let browserPostfix;
if (browser == 'firefox') {
browserPostfix = 'ff';
} else {
browserPostfix = browser;
}
const destDir = path.join(__dirname, `../dist-${browserPostfix}`);
const zipDir = path.join(__dirname, '../dist-zip');
const {name, version} = extractExtensionData(browserPostfix);
const {name, version} = extractExtensionData(); // collapse spaces and dashes into single dash
const zipFilename = `${name}-${version}-${browser}.zip`; const baseFilename = `${name.replace(/[ -]+/g, '-')}-${version}`;
makeDestZipDirIfNotExists(); let realZipDir;
buildZip(DEST_DIR, DEST_ZIP_DIR, zipFilename) if (!!testingOrNightly) {
realZipDir = path.join(zipDir, version);
} else {
realZipDir = path.join(zipDir);
}
const zipFilename = `${baseFilename}-${browser}.zip`;
try {
makeDirIfNotExists(realZipDir, {recursive: true});
} catch (e) {
console.error('Failed to make directory.\nDirectory we wanted to make', realZipDir, '\nerror we got:\n', e)
return 1;
}
buildZip(destDir, realZipDir, zipFilename)
.then(() => console.info('OK')) .then(() => console.info('OK'))
.catch(console.err); .catch(console.err);
}; };

View File

@ -2,4 +2,46 @@
# makes a zip file with human-readable source code that we need to upload to AMO # makes a zip file with human-readable source code that we need to upload to AMO
# since webpack minifies stuff # since webpack minifies stuff
zip -r dist-zip/uw-amo-source.zip README-AMO.md .babelrc package.json package-lock.json webpack.config.js src/
# first, we collect npm/node versions:
# (NOTE: the last bit is necessary to remove ANSI color codes from output)
NODE_VERSION=`node --version`
NPM_VERSION=`npm --version`
LINUX_VERSION="$(uname -a | sed 's/\//\\\//g')"
# copy REAMDE to /tmp for processing
cp README-AMO.md /tmp/README-AMO.md
# replace placeholders with proper software versions
sed -i "s/%%NODE_VERSION%%/${NODE_VERSION}/g" /tmp/README-AMO.md
sed -i "s/%%NPM_VERSION%%/${NPM_VERSION}/g" /tmp/README-AMO.md
sed -i "s/%%LINUX_VERSION%%/${LINUX_VERSION}/g" /tmp/README-AMO.md
# add files to archive
zip -r dist-zip/uw-amo-source.zip /tmp/README-AMO.md .babelrc package.json package-lock.json webpack.config.js scripts/ src/
# rename /tmp/README-AMO.md to README.md
printf "@ tmp/README-AMO.md\n@=README.md\n" | zipnote -w dist-zip/uw-amo-source.zip
# printout for debugging purposes:
echo ""
echo "—————— AMO SOURCE PREPARATION FINISHED ——————"
echo " Debug info:"
echo ""
echo "node --version:"
node --version
echo ""
echo "npm --version"
npm --version
echo ""
echo "uname -a"
uname -a
echo ""
echo ""
echo "extracted variables:"
echo "NODE_VERSION: ${NODE_VERSION}"
echo "NPM_VERSION: ${NPM_VERSION}"
echo "UNAME: ${LINUX_VERSION}"
echo ""
echo ""
echo "—————— EXTENSION PACKAGES READY FOR UPLOAD ——————"

View File

@ -3,11 +3,9 @@
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const BUNDLE_DIR = path.join(__dirname, '../dist'); const BUNDLE_DIR = path.join(__dirname, `../dist-${process.env.BROWSER === 'firefox' ? 'ff' : process.env.BROWSER}`);
const bundles = [ const bundles = [
'background.js', // 'csui/csui-popup.js',
'popup/popup.js',
'options/options.js',
]; ];
const evalRegexForProduction = /;([a-z])=function\(\){return this}\(\);try{\1=\1\|\|Function\("return this"\)\(\)\|\|\(0,eval\)\("this"\)}catch\(t\){"object"==typeof window&&\(\1=window\)}/g; const evalRegexForProduction = /;([a-z])=function\(\){return this}\(\);try{\1=\1\|\|Function\("return this"\)\(\)\|\|\(0,eval\)\("this"\)}catch\(t\){"object"==typeof window&&\(\1=window\)}/g;

57
scripts/reproducability-test.sh Executable file
View File

@ -0,0 +1,57 @@
#!/bin/bash
### VARIABLES
REPRODUCABILITY=5 # how many times we need to build with same hash
### TEST STARTS HERE ###
CURRENT_DIR=$(pwd)
if [[ $(echo $0 | awk -F"/" '{print NF-1}') -eq 1 ]] ; then
# we need to go one directory up
cd ..
fi
# cleanup after self if we already ran and previous test crashed or
# something before it cleaned up after itself.
rm -rf /tmp/uw-test-runs/* 2>/dev/null # we not interested if fails
# make new folder if it doesn't exist
mkdir -p /tmp/uw-test-runs
for ((run=0;run<$REPRODUCABILITY;run++)) ; do
echo ""
echo "---- run ${run} ----"
# save hash and then run build. Save hash.
OLD_HASH=$HASH
HASH=$(npm run build 2>/dev/null | grep "Hash:")
# move build file to /tmp, where it'll be saved for later
mv dist-ff /tmp/uw-test-runs/${run}
# skip comparisons with previous tests on first run,
# cos we don't have anything to compare against yet
if [[ $run -ne 0 ]] ; then
if [[ "$OLD_HASH" == "$HASH" ]] ; then
echo "Hashes ok${HASH##*:}"
else
echo "Webpack hashes do not match! (${OLD_HASH##*:} <---> ${HASH##*:})"
fi
echo "Diff test (no output=ok):"
diff -qr /tmp/uw-test-runs/${prev_run} /tmp/uw-test-runs/${run}
else
echo "Webpack hash: $HASH"
fi
# save id of previous run
prev_run=$run
done
# clean up after self
rm -rf /tmp/uw-test-runs
# restore dir
cd "$CURRENT_DIR"

View File

@ -1,224 +0,0 @@
<template>
<div class="action flex flex-column">
<div class="flex flex-row action-name-cmd-container">
<div class="">
</div>
<div class="flex action-name">
<span class="icon red" @click="removeAction()">🗙</span> &nbsp; &nbsp;
<span class="icon" @click="editAction()">🖉</span> &nbsp; &nbsp;
{{action.name}}
</div>
</div>
<div class="flex flex-row command-details">
<div class="flex flex-column cmd-container">
<div class="flex bold">
Command:
</div>
<div class="flex cmdlist">
{{parseCommand(action.cmd)}}
</div>
</div>
<!-- SCOPES -->
<div class="flex flex-column scopes-container">
<div class="flex bold">Popup scopes:</div>
<!-- global scope -->
<div v-if="action.scopes.global"
class="flex flex-row scope-row"
>
<div class="flex scope-scope">
Global:
</div>
<div class="flex scope-row-label scope-visible">
Visible?&nbsp;<span class="scope-row-highlight">{{action.scopes.global.show ? 'Yes' : 'No'}}</span>
</div>
<div v-if="action.scopes.global.show"
class="flex scope-row-label scope-button-label"
>
Button label?&nbsp;
<span class="scope-row-highlight">
{{action.scopes.global.label ? action.scopes.global.label : (action.label ? action.label : '')}}
</span>
</div>
<div class="flex scope-row-label">
Keyboard shortcut:&nbsp;
<span class="scope-row-highlight">
{{action.scopes.global.shortcut ? parseActionShortcut(action.scopes.global.shortcut[0]) : 'None'}}
</span>
</div>
</div>
<!-- site scope -->
<div v-if="action.scopes.site"
class="flex flex-row scope-row"
>
<div class="flex scope-scope">
Site:
</div>
<div class="flex scope-row-label scope-visible">
Visible?&nbsp;<span class="scope-row-highlight">{{action.scopes.site.show ? 'Yes' : 'No'}}</span>
</div>
<div v-if="action.scopes.site.show"
class="flex scope-row-label scope-button-label"
>
Button label?&nbsp;
<span class="scope-row-highlight">
{{action.scopes.site.label ? action.scopes.site.label : (action.label ? action.label : '')}}
</span>
</div>
<div class="flex scope-row-label">
Keyboard shortcut:&nbsp;
<span class="scope-row-highlight">
{{action.scopes.site.shortcut ? parseActionShortcut(action.scopes.site.shortcut[0]) : 'None'}}
</span>
</div>
</div>
<!-- page scope -->
<div v-if="action.scopes.page"
class="flex flex-row scope-row"
>
<div class="flex scope-scope">
Page:
</div>
<div class="flex scope-row-label scope-visible">
Visible?&nbsp;<span class="scope-row-highlight">{{action.scopes.page.show ? 'Yes' : 'No'}}</span>
</div>
<div v-if="action.scopes.page.show"
class="flex scope-row-label scope-button-label"
>
Button label?&nbsp;
<span class="scope-row-highlight">
{{action.scopes.page.label ? action.scopes.page.label : (action.label ? action.label : '')}}
</span>
</div>
<div class="flex scope-row-label">
Keyboard shortcut:&nbsp;
<span class="scope-row-highlight">
{{action.scopes.page.shortcut ? parseActionShortcut(action.scopes.page.shortcut[0]) : 'None'}}
</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Stretch from '../enums/stretch.enum';
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';
export default {
data () {
return {
Stretch: Stretch
}
},
created () {
},
props: {
action: Object,
index: Number,
},
methods: {
parseActionShortcut(shortcut) {
return KeyboardShortcutParser.parseShortcut(shortcut);
},
parseCommand(cmd) {
let cmdstring = '';
for (const c of cmd) {
cmdstring += `${c.action} ${c.arg}${c.persistent ? ' persistent' : ''}; `;
}
return cmdstring;
},
editAction() {
this.$emit('edit');
},
removeAction() {
this.$emit('remove');
}
}
}
</script>
<style lang="scss" scoped>
@import '../../res/css/colors.scss';
.action {
cursor: pointer;
}
.action .command-details {
height: 0px;
max-height: 0px;
transition: max-height 0.5s ease;
overflow: hidden;
transition: height 0.5s ease;
}
.action:hover .command-details {
height: auto;
max-height: 200px;
transition: max-height 0.5s ease;
}
.action-name-cmd-container {
padding: 1rem;
}
.action-name {
font-size: 1.5rem;
font-weight: 300;
color: $text-normal;
}
.action-name:hover {
color: lighten($primary-color, 20%);
}
.red {
color: $primary-color !important;
}
.cmd-container {
width: 13.37rem;
}
.cmdlist {
font-family: 'Overpass Mono';
font-size: 0.9rem;
color: $text-dim;
}
.bold {
font-weight: 600;
}
.scope-scope {
width: 5rem;
text-align: right !important;
color: $secondary-color;
}
.scope-visible {
width: 7rem;
}
.scope-button-label {
width: 16rem;
}
.scope-row-label {
color: $text-dark;
}
.scope-row-highlight {
color: $text-normal;
}
</style>

View File

@ -1,76 +0,0 @@
<template>
<tr>
<td class="cmd monospace">{{parseCommand(action.cmd)}}</td>
<td class="">{{action.label ? action.label : ""}}</td>
<td class="shortcut center-text">{{parseActionShortcut(action)}}</td>
<td class="center-text">
<div class="checkbox-container">
<span class="checkbox"
:class="{'checkbox-checked': (action.shortcut && action.shortcut.length &&
(action.shortcut[0].onMouseMove || action.shortcut[0].onClick ||
action.shortcut[0].onScroll))}"
></span>
</div>
</td>
<td class="center-text">
<div class="checkbox-container">
<span class="checkbox"
:class="{'checkbox-checked': action.popup_global}"
>
</span>
</div>
</td>
<td class="center-text">
<div class="checkbox-container">
<span class="checkbox"
:class="{'checkbox-checked': action.popup_site}"
></span>
</div>
</td>
<td class="center-text">
<div class="checkbox-container">
<span class="checkbox"
:class="{'checkbox-checked': action.popup}"
></span>
</div>
</td>
<td class="center-text">
<div class="checkbox-container">
<span class="checkbox"
:class="{'checkbox-checked': action.ui}"
></span>
</div>
</td>
<td>{{action.ui_path ? action.ui_path : ""}}</td>
</tr>
</template>
<script>
import Stretch from '../enums/stretch.enum';
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
export default {
data () {
return {
Stretch: Stretch
}
},
created () {
},
props: {
action: Object
},
methods: {
parseActionShortcut(action) {
return KeyboardShortcutParser.parseShortcut(action.shortcut[0]);
},
parseCommand(cmd) {
let cmdstring = '';
for(const c of cmd) {
cmdstring += `${c.action} ${c.arg}${c.persistent ? ' persistent' : ''}; `;
}
return cmdstring;
}
}
}
</script>

View File

@ -1,17 +0,0 @@
<template>
<div class="button center-text flex"
:class="{'setting-selected': selected, 'w24': fixedWidth, 'flex-auto': !fixedWidth }"
>
{{label}}
</div>
</template>
<script>
export default {
props: {
fixedWidth: Boolean,
selected: Boolean,
label: String,
}
}
</script>

View File

@ -1,53 +0,0 @@
<template>
<div class="qsb flex flex-row flex-cross-center flex-center flex-nogrow"
:class="{
'id': selector.startsWith('#'),
'class': selector.startsWith('.'),
}">
<div class="flex mt2px">{{selector}}</div> <span class="flex closeButton" @click="remove">×</span>
</div>
</template>
<script>
export default {
props: {
selector: String,
},
methods: {
remove() {
this.$emit('remove');
}
}
}
</script>
<style>
.mt2px {
margin-top: 7px;
}
.id {
border: 1px solid #d00;
background-color: rgba(216, 94, 24, 0.25)
}
.class {
border: 1px solid #33d;
background-color: rgba(69, 69, 255, 0.25)
}
.closeButton {
margin-top: 2px;
margin-left: 4px;
margin-right: 4px;
font-size: 1.5em;
color: #e00;
}
.closeButton:hover {
cursor: pointer;
color: #fa6;
}
.qsb {
cursor:default;
margin-left: 3px;
margin-right: 3px;
margin-bottom: 2px;
}
</style>

View File

@ -1,87 +0,0 @@
<template>
<div class="flex flex-column">
<div v-if="!editing && !adding" class="flex flex-row">
<div class="">
<b>Query selector:</b> {{qs.string}}<br/>
<b>Additional CSS:</b> {{qs.css || 'no style rules'}}
</div>
<div class="flex flex-column flex-nogrow">
<a @click="editing = true">Edit</a>
<a @click="$emit('delete')">Delete</a>
</div>
</div>
<div v-else class="flex flex-row">
<div class="flex flex-column">
<div class="flex flex-row">
<div class="flex label-secondary form-label">
Query selector:
</div>
<div class="flex flex-input">
<input type="text"
v-model="qs.string"
/>
</div>
</div>
<div class="flex flex-row">
<div class="flex label-secondary form-label">
Additional CSS:
</div>
<div class="flex flex-input">
<input type="text"
v-model="qs.css"
/>
</div>
</div>
</div>
<div>
<a v-if="editing" @click="cancelEdit">Cancel</a>
<a v-if="adding" @click="clear">Clear</a>
<a @click="save">Save</a>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
qs: {string: '', css: ''},
editing: false,
}
},
props: {
querySelector: Object,
adding: {
type: Boolean,
required: false,
default: false
}
},
watch: {
querySelector(val) {
this.qs = JSON.parse(JSON.stringify(val));
}
},
methods: {
save() {
if (this.adding) {
this.$emit('create', this.qs);
this.qs = {string: '', css: ''};
} else {
this.emit$('update', this.qs)
}
},
cancelEdit() {
this.qs = JSON.parse(JSON.stringify(this.querySelector));
},
clear() {
this.qs = {string: '', css: ''};
}
}
}
</script>
<style>
</style>

View File

@ -1,30 +0,0 @@
<template>
<div class="flex flex-column flex-center">
<div class="flex flex-self-center">
{{label}}
</div>
<div class="flex dark flex-self-center">
<small>
{{shortcut ? `(${shortcut})` : ''}}
</small>
</div>
</div>
</template>
<script>
export default {
props: {
label: String,
shortcut: String
}
}
</script>
<style scoped>
.center-text {
text-align: center;
}
.dark {
opacity: 50%;
}
</style>

View File

@ -0,0 +1,14 @@
let Notifications = Object.freeze({
'TEST_NOTIFICATION': {
icon: 'card-text',
text: 'This is a test notification.',
timeout: -1,
},
'AARD_DRM': {
icon: 'exclamation-triangle',
text: '<b>Autodetection may not be able to run on this video.</b> On sites that utilize DRM you will have to set aspect ratio manually.',
timeout: 5000,
}
});
export default Notifications;

View File

@ -0,0 +1,7 @@
enum AntiGradientMode {
Disabled = 0,
Lax = 1,
Strict = 2
}
export default AntiGradientMode;

View File

@ -0,0 +1,15 @@
enum AspectRatioType {
Default = -3, // inherit from global settings
Cycle = -2,
Initial = -1, // page default
Reset = 0, // reset to initial
Automatic = 1, // we want to request automatic aspect ratio detection
FitWidth = 2, // legacy/dynamic = fit to width
FitHeight = 3, // legacy/dynamic = fit to height
Fixed = 4, // pre-determined aspect ratio
Manual = 5, // ratio achieved by zooming in/zooming out
AutomaticUpdate = 6, // set by Aard
Cover = 7, // replaces FitWidth and FitHeight
}
export default AspectRatioType;

View File

@ -0,0 +1,9 @@
enum CropModePersistence {
Default = -1,
Disabled = 0,
UntilPageReload = 1,
CurrentSession = 2,
Forever = 3,
}
export default CropModePersistence;

View File

@ -0,0 +1,8 @@
enum EmbeddedContentSettingsOverridePolicy {
Always = 2,
UseAsDefault = 1,
Default = 0,
Never = -1,
}
export default EmbeddedContentSettingsOverridePolicy;

View File

@ -0,0 +1,9 @@
enum ExtensionMode {
Disabled = -1,
Default = 0,
FullScreen = 1,
Theater = 2,
All = 3
}
export default ExtensionMode;

View File

@ -0,0 +1,5 @@
export enum InputHandlingMode {
Disabled = -1,
Default = 0,
Enabled = 1, // enable, but don't override site shortcuts
}

View File

@ -0,0 +1,10 @@
enum LegacyExtensionMode {
AutoDisabled = -2,
Disabled = -1,
Default = 0,
Whitelist = 1,
Basic = 2,
Enabled = 3,
};
export default LegacyExtensionMode;

View File

@ -0,0 +1,5 @@
export enum PlayerDetectionMode {
Auto = 0,
AncestorIndex = 1,
QuerySelectors = 2,
};

View File

@ -0,0 +1,9 @@
export enum SiteSupportLevel {
OfficialSupport = 'official',
CommunitySupport = 'community',
UserDefined = 'user-defined',
BetaSupport = 'testing',
OfficialBlacklist = 'officially-disabled',
Unknown = 'unknown',
UserModified = 'modified',
}

View File

@ -0,0 +1,11 @@
enum StretchType {
NoStretch = 0,
Basic = 1,
Hybrid = 2,
Conditional = 3,
Fixed = 4,
FixedSource = 5,
Default = -1
};
export default StretchType;

View File

@ -0,0 +1,11 @@
enum VideoAlignmentType {
Left = 0,
Center = 1,
Right = 2,
Top = 3,
Bottom = 4,
Default = -1,
Custom = -2,
};
export default VideoAlignmentType;

View File

@ -1,7 +0,0 @@
var AntiGradientMode = Object.freeze({
Disabled: 0,
Lax: 1,
Strict: 2
});
export default AntiGradientMode;

View File

@ -1,10 +0,0 @@
var AspectRatio = Object.freeze({
Initial: -1,
Reset: 0,
Automatic: 1,
FitWidth: 2,
FitHeight: 3,
Fixed: 4,
});
export default AspectRatio;

View File

@ -1,10 +0,0 @@
var ExtensionMode = Object.freeze({
AutoDisabled: -2,
Disabled: -1,
Default: 0,
Whitelist: 1,
Basic: 2,
Enabled: 3,
});
export default ExtensionMode;

View File

@ -1,9 +0,0 @@
var Stretch = Object.freeze({
NoStretch: 0,
Basic: 1,
Hybrid: 2,
Conditional: 3,
Default: -1
});
export default Stretch;

View File

@ -1,8 +0,0 @@
var VideoAlignment = Object.freeze({
Left: 0,
Center: 1,
Right: 2,
Default: -1
});
export default VideoAlignment;

View File

@ -0,0 +1,13 @@
import AspectRatioType from '../enums/AspectRatioType.enum';
export enum ArVariant {
Crop = undefined,
Zoom = 1
}
export interface Ar {
type: AspectRatioType,
ratio?: number,
variant?: ArVariant,
offset?: number,
}

View File

@ -0,0 +1,31 @@
import { CommandInterface, InPlayerUIOptions } from '@src/common/interfaces/SettingsInterface';
export interface MenuItemBaseConfig {
label?: string;
subitems?: MenuItemConfig[];
command?: CommandInterface,
action?: () => void;
customHTML?: HTMLElement | string;
customId?: string;
customClassList?: string;
}
export type MenuItemConfig = MenuItemBaseConfig & ({label: string} | {customHTML: HTMLElement | string});
export enum MenuPosition {
TopLeft = 'top-left',
Left = 'left',
BottomLeft = 'bottom-left',
Top = 'top',
Bottom = 'bottom',
TopRight = 'top-right',
Right = 'right',
BottomRight = 'bottom-right',
}
export interface MenuConfig {
isGlobal?: boolean;
ui: InPlayerUIOptions;
options?: {forceShow?: boolean};
items: MenuItemConfig[];
}

View File

@ -0,0 +1,43 @@
import { CommsOrigin } from '@/ext/module/comms/CommsClient';
import type { Runtime } from 'chrome';
export interface EventBusCommand {
isGlobal?: boolean,
source?: any,
function: (commandData: any, context?: EventBusContext) => void | Promise<void>
}
export interface EventBusMessage<T = unknown> {
command: string;
config?: T;
context?: EventBusContext;
}
export interface EventBusContext {
stopPropagation?: boolean;
origin?: CommsOrigin;
frameUrl?: string;
// tab?: number;
// frame?: number;
// port?: string;
visitedBusses?: string[];
commandId?: string;
comms?: {
forwardTo?: 'all' | 'active' | 'popup' | 'contentScript' | 'all-frames';
sender?: Runtime.MessageSender;
port?: Runtime.Port;
// frame?: any;
sourceFrame?: {
tabId: number;
frameId: number;
};
};
borderCrossings?: {
commsServer?: boolean,
iframe?: boolean,
};
}

View File

@ -0,0 +1,8 @@
import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
export interface HostInfo {
host: string,
hasVideo: boolean,
minEnvironment: ExtensionEnvironment,
maxEnvironment: ExtensionEnvironment
};

View File

@ -0,0 +1,6 @@
import { EventBusMessage } from '@src/common/interfaces/EventBusMessage.interface';
export interface IframeTunnelPayload {
action: string,
payload: EventBusMessage,
}

View File

@ -0,0 +1,19 @@
import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum';
import { Ar } from '@src/common/interfaces/ArInterface';
import { Stretch } from '@src/common/interfaces/StretchInterface';
export interface ScalingParamsBroadcast {
effectiveZoom: {
x: number,
y: number
},
videoAlignment: {
x: VideoAlignmentType,
y: VideoAlignmentType,
xPos?: number,
yPos?: number,
}
lastAr: Ar,
manualZoom: boolean,
stretch: Stretch,
}

View File

@ -0,0 +1,525 @@
import { AardPollingOptions } from '@src/ext/module/aard/enums/aard-polling-options.enum'
import { AardSubtitleCropMode } from '@src/ext/module/aard/enums/aard-subtitle-crop-mode.enum'
import { SiteSupportLevel } from '@src/common/enums/SiteSupportLevel.enum';
import AspectRatioType from '@src/common/enums/AspectRatioType.enum'
import CropModePersistence from '@src/common/enums/CropModePersistence.enum'
import EmbeddedContentSettingsOverridePolicy from '@src/common/enums/EmbeddedContentSettingsOverridePolicy.enum'
import ExtensionMode from '@src/common/enums/ExtensionMode.enum'
import StretchType from '@src/common/enums/StretchType.enum'
import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum'
import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum';
import { InputHandlingMode } from '@src/common/enums/InputHandlingMode.enum';
import { MenuPosition } from '@src/common/interfaces/ClientUiMenu.interface';
export enum ExtensionEnvironment {
Normal = ExtensionMode.All,
Theater = ExtensionMode.Theater,
Fullscreen = ExtensionMode.FullScreen,
}
export interface DevUiConfig {
aardDebugOverlay: {
showOnStartup: boolean,
showDetectionDetails: boolean,
}
}
export interface KeyboardShortcutInterface {
key?: string,
code?: string,
ctrlKey?: boolean,
metaKey?: boolean,
altKey?: boolean,
shiftKey?: boolean,
onKeyUp?: boolean,
onKeyDown?: boolean,
onMouseMove?: boolean,
}
interface ActionScopeInterface {
show: boolean,
label?: string, // example override, takes precedence over default label
shortcut?: KeyboardShortcutInterface[],
}
interface RestrictionsSettings {
disableOnSmallPlayers?: boolean; // Whether ultrawidify should disable itself when the player is small
minAllowedWidth?: number; // if player is less than this many px wide, ultrawidify will disable itself
minAllowedHeight?: number; // if player is less than this many px tall, ultrawidify will disable itself
onlyAllowInFullscreen?: boolean; // if enabled, ultrawidify will be disabled when not in full screen regardless of what previous two options say
onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen
}
export interface CommandInterface {
action: string,
label: string,
comment?: string,
arguments?: any,
shortcut?: KeyboardShortcutInterface,
internalOnly?: boolean,
actionId?: string,
}
export type SettingsReloadComponent = 'PlayerData' | 'VideoData';
export type SettingsReloadFlags = true | SettingsReloadComponent;
export interface AardSubtitleScanOptions {
subtitleCropMode: AardSubtitleCropMode,
resumeAfter: number, // resume after X ms of no subtitle
scanSpacing: number, // repeat subtitle scan every _n_ lines
scanMargin: number, // % of pixels (from edge to start area) that we skip while scanning.
// While technically anything between 0 and 0.5 is valid, the value
// should be somewhere between 0.1-0.3 in order for subtitle scan to
// work properly.
maxValidLetter: number, // if letter is longer than this, something's off.
subtitleSubpixelThresholdOn: number,
subtitleSubpixelThresholdOff: number,
minDetections: number,
minImageLineDetections: number,
maxPotentialSubtitleMisalignment: number, // how many pixels off-center can "potential subtitle" be
refiningScanSpacing: number, // must be base-2
refiningScanInitialIterations: number,
}
export interface AardSettings {
aardType: 'webgl' | 'legacy' | 'auto';
useLegacy: boolean,
earlyStopOptions: {
stopAfterFirstDetection: boolean;
stopAfterTimeout: boolean;
stopTimeout: number;
},
polling: {
runInBackgroundTabs: AardPollingOptions;
runOnSmallVideos: AardPollingOptions;
}
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
// Any more and we don't adjust ar.
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
timers: { // autodetection frequency
playing: number, // while playing
playingReduced: number, // while video/player element has insufficient size
paused: number, // while paused
error: number, // after error
minimumTimeout: number,
tickrate: number, // 1 tick every this many milliseconds
},
subtitles: AardSubtitleScanOptions,
autoDisable: { // settings for automatically disabling the extension
onFirstChange: boolean,
ifNotChanged: boolean,
ifNotChangedTimeout: number,
ifSubtitles: boolean;
},
canvasDimensions: {
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
// it's not used to detect aspect ratio by itself, so it can be tiny af
width: number,
height: number,
},
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
// at the expense of performance
width: number,
height: number,
},
},
blackLevels: {
defaultBlack: number, // By default, pixels darker than this are considered black.
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
blackTolerance: number, // If pixel is more than this much brighter than blackLevel, it's considered not black
// It is not considered a valid image detection if gradient detection is enabled
imageDelta: number, // When gradient detection is enabled, pixels this much brighter than black skip gradient detection
}
sampling: {
edgePosition: number; // % of width (max 0.33). Pixels up to this far away from either edge may contain logo.
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
randomCols: number, // we add this many randomly selected columns to the static columns
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks,
},
// pls deprecate and move things used
edgeDetection: {
gradientThreshold: number, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
gradientTestMinDelta: number, // if difference between test row and before row is MORE than this -> not gradient
gradientTestMinDeltaAfter: number, // if difference between test row and after row is LESS than this -> not gradient
gradientTestMaxDeltaAfter: number, // if difference between test row and after row is MORE than this -> not gradient
maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height)
minValidImage: number, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
maxEdgeSegments: number, // if edge has more than this many segments, we consider it unreliable
minEdgeSegmentSize: number,
averageEdgeThreshold: number, // average edge must be this many px
},
letterboxOrientationScan: {
letterboxLimit: number, // how many non-black pixels we can detect before ruling out letterbox
pillarboxLimit: number, // how many non-black pixels we can detect before ruling out pillarbox
}
pillarTest: {
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
allowMisaligned: number // left and right edge can vary this much (%)
},
textLineTest: {
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
// with text. This value is relative to canvas width (%)
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
// is over 50% of the canvas width
testRowOffset: number // we test this % of height from detected edge
}
}
export interface AardLegacySettings {
aardType: 'webgl' | 'legacy' | 'auto';
earlyStopOptions: {
stopAfterFirstDetection: boolean;
stopAfterTimeout: boolean;
stopTimeout: number;
},
polling: {
runInBackgroundTabs: AardPollingOptions;
runOnSmallVideos: AardPollingOptions;
}
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
// Any more and we don't adjust ar.
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
timers: { // autodetection frequency
playing: number, // while playing
playingReduced: number, // while video/player element has insufficient size
paused: number, // while paused
error: number, // after error
minimumTimeout: number,
tickrate: number, // 1 tick every this many milliseconds
},
subtitles: AardSubtitleScanOptions,
autoDisable: { // settings for automatically disabling the extension
onFirstChange: boolean,
ifNotChanged: boolean,
ifNotChangedTimeout: number,
ifSubtitles: boolean;
},
canvasDimensions: {
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
// it's not used to detect aspect ratio by itself, so it can be tiny af
width: number,
height: number,
},
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
// at the expense of performance
width: number,
height: number,
},
},
blackLevels: {
defaultBlack: number, // By default, pixels darker than this are considered black.
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
blackTolerance: number, // If pixel is more than this much brighter than blackLevel, it's considered not black
// It is not considered a valid image detection if gradient detection is enabled
imageDelta: number, // When gradient detection is enabled, pixels this much brighter than black skip gradient detection
}
sampling: {
edgePosition: number; // % of width (max 0.33). Pixels up to this far away from either edge may contain logo.
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
randomCols: number, // we add this many randomly selected columns to the static columns
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks,
},
// pls deprecate and move things used
edgeDetection: {
slopeTestWidth: number,
gradientTestSamples: number, // we check this many pixels below (or above) the suspected edge to check for gradient
gradientTestBlackThreshold: number, // if pixel in test sample is brighter than that, we aren't looking at gradient
gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
thresholds: {
edgeDetectionLimit: number, // during scanning of the edge, quit after edge gets detected at this many points
minQualitySingleEdge: number, // At least one of the detected must reach this quality
minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality)
}
gradientThreshold: number, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
gradientTestMinDelta: number, // if difference between test row and before row is MORE than this -> not gradient
gradientTestMinDeltaAfter: number, // if difference between test row and after row is LESS than this -> not gradient
gradientTestMaxDeltaAfter: number, // if difference between test row and after row is MORE than this -> not gradient
maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height)
// Previous iteration variables VVVV
sampleWidth: number, // we take a sample this wide for edge detection
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
confirmationThreshold: number, //
singleSideConfirmationThreshold: number, // we need this much edges (out of all samples, not just edges) in order
// to confirm an edge in case there's no edges on top or bottom (other
// than logo, of course)
logoThreshold: number, // if edge candidate sits with count greater than this*all_samples, it can't be logo
// or watermark.
edgeTolerancePx?: number, // we check for black edge violation this far from detection point
edgeTolerancePercent?: number, // we check for black edge detection this % of height from detection point. unused
middleIgnoredArea: number, // we ignore this % of canvas height towards edges while detecting aspect ratios
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
// continue with search. It's pointless, because black edge is higher/lower than we
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
edgeMismatchTolerancePx: number,// corners and center are considered equal if they differ by at most this many px
minValidImage: number, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
maxEdgeSegments: number, // if edge has more than this many segments, we consider it unreliable
minEdgeSegmentSize: number,
averageEdgeThreshold: number, // average edge must be this many px
},
letterboxOrientationScan: {
letterboxLimit: number, // how many non-black pixels we can detect before ruling out letterbox
pillarboxLimit: number, // how many non-black pixels we can detect before ruling out pillarbox
}
pillarTest: {
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
allowMisaligned: number // left and right edge can vary this much (%)
},
textLineTest: {
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
// with text. This value is relative to canvas width (%)
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
// is over 50% of the canvas width
testRowOffset: number // we test this % of height from detected edge
}
}
interface DevSettings {
loadFromSnapshot: boolean,
}
export interface InPlayerUIOptions {
activatorAlignment: MenuPosition,
minEnabledWidth: number, // don't show UI if player is narrower than % of screen width
minEnabledHeight: number, // don't show UI if player is narrower than % of screen height
activation: 'player' | 'trigger-zone' | 'distance' | 'none', // what needs to be hovered in order for UI to be visible
activateWithCtrl: boolean,
activationDistance: number,
activationDistanceUnits: '%' | 'px',
activatorPadding: {x: number, y: number}
activatorPaddingUnit: {x: '%' | 'px', y: '%' | 'px'},
triggerZoneDimensions: { // how large the trigger zone is (relative to player size)
width: number
height: number,
offsetX: number, // fed to translateX(offsetX + '%'). Valid range [-100, 0]
offsetY: number // fed to translateY(offsetY + '%'). Valid range [-100, 100]
},
};
interface SettingsInterface {
_updateFlags?: {
requireReload?: SettingsReloadFlags,
forSite?: string
}
dev: DevSettings,
aardLegacy: AardLegacySettings,
aard: AardSettings,
ui: {
inPlayer: InPlayerUIOptions,
devMode?: boolean,
dev: DevUiConfig,
}
restrictions?: RestrictionsSettings;
crop: {
default: any;
},
stretch: {
default: any;
conditionalDifferencePercent: number // black bars less than this wide will trigger stretch
// if mode is set to '1'. 1.0=100%
},
kbm: { // TODO: we dont use this anymore
enabled: boolean, // if keyboard/mouse handler service will run
keyboardEnabled: boolean, // if keyboard shortcuts are processed
mouseEnabled: boolean, // if mouse movement is processed
}
zoom: {
minLogZoom: number,
maxLogZoom: number,
announceDebounce: number // we wait this long before announcing new zoom
},
miscSettings: {
mousePan: {
enabled: boolean
},
mousePanReverseMouse: boolean,
defaultAr?: any
},
resizer: {
setStyleString: {
maxRetries: number,
retryTimeout: number
}
},
pageInfo: {
timeouts: {
urlCheck: number,
rescan: number
}
},
pan?: any,
version?: string,
preventReload?: boolean,
lastModified?: Date,
// -----------------------------------------
// ::: MITIGATIONS :::
// -----------------------------------------
// Settings for browser bug workarounds.
mitigations?: {
zoomLimit?: {
enabled?: boolean,
fullscreenOnly?: boolean,
limit?: number,
}
}
// This object fulfills the same purpose as 'actions', but is written in less retarded and overly
// complicated way. Hopefully it'll be easier to maintain it that way.
commands?: {
crop?: CommandInterface[],
stretch?: CommandInterface[],
zoom?: CommandInterface[],
pan?: CommandInterface[],
internal?: CommandInterface[],
},
whatsNewChecked: boolean,
newFeatureTracker: any,
// -----------------------------------------
// ::: SITE CONFIGURATION :::
// -----------------------------------------
// Config for a given page:
//
// <hostname> : {
// status: <option> // should extension work on this site?
// arStatus: <option> // should we do autodetection on this site?
//
// defaultAr?: <ratio> // automatically apply this aspect ratio on this side. Use extension defaults if undefined.
// stretch? <stretch mode> // automatically stretch video on this site in this manner
// videoAlignment? <left|center|right>
//
// type: <official|community|user> // 'official' — blessed by Tam.
// // 'community' — blessed by reddit.
// // 'user' — user-defined (not here)
// override: <true|false> // override user settings for this site on update
// }
//
// Valid values for options:
//
// status, arStatus, statusEmbedded:
//
// * enabled — always allow, full
// * basic — allow, but only the basic version without playerData
// * default — allow if default is to allow, block if default is to block
// * disabled — never allow
//
sites: {
[x: string]: SiteSettingsInterface,
}
}
export interface SiteSettingsInterface {
notes?: string; // any special things related to this site.
enable: ExtensionMode;
enableAard: ExtensionMode;
enableKeyboard: InputHandlingMode;
enableUI: ExtensionMode;
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy; // presumed to be 'Use as default' if not defined
overrideWhenEmbedded?: EmbeddedContentSettingsOverridePolicy;
autocreated?: boolean;
type?: SiteSupportLevel;
defaultType: SiteSupportLevel;
// must be defined in @global and @empty
persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment
defaults?: { // must be defined in @global and @empty
crop?: {type: AspectRatioType, [x: string]: any},
stretch?: {type: StretchType, ratio?: number},
alignment?: {x: VideoAlignmentType, y: VideoAlignmentType},
}
cropModePersistence?: CropModePersistence;
stretchModePersistence?: CropModePersistence;
alignmentPersistence?: CropModePersistence;
playerAutoConfig?: PlayerAutoConfigInterface;
activeDOMConfig?: string;
DOMConfig?: { [x: string]: SiteDOMSettingsInterface };
// the following script are for extension caching and shouldn't be saved.
// if they _are_ saved, they will be overwritten
currentDOMConfig?: SiteDOMSettingsInterface;
// the following fields are for use with extension update script
override?: boolean; // whether settings for this site will be overwritten by extension upgrade script
}
export interface PlayerAutoConfigInterface {
modified: boolean;
initialIndex: number;
currentIndex: number;
}
export interface SiteDOMSettingsInterface {
type: SiteSupportLevel; // 'official' | 'community' | 'user-defined' | 'modified' | undefined;
elements?: {
player?: PlayerDOMSettingsInterface,
video?: PlayerDOMSettingsInterface,
};
customCss?: string;
periodicallyRefreshPlayerElement?: boolean;
// the following script are for extension caching and shouldn't be saved.
// if they _are_ saved, they will be overwritten
anchorElementIndex?: number;
anchorElement?: HTMLElement;
}
export interface PlayerDOMSettingsInterface {
detectionMode: PlayerDetectionMode,
allowAutoFallback: boolean,
ancestorIndex?: number,
querySelectors?: string,
}
export interface SiteDOMElementSettingsInterface {
manual?: boolean;
querySelectors?: string;
index?: number; // previously: useRelativeAncestor + videoAncestor
mode?: 'index' | 'qs';
}
export default SettingsInterface;

View File

@ -0,0 +1,7 @@
import StretchType from '../enums/StretchType.enum';
export interface Stretch {
type: StretchType,
ratio?: number,
limit?: number,
}

View File

@ -0,0 +1,33 @@
/**
* For some reason, Chrome really doesn't like when chrome.runtime
* methods are wrapped inside a ES6 proxy object. If 'port' is a
* ES6 Proxy of a Port object that `chrome.runtime.connect()` creates,
* then Chrome will do bullshits like `port.sendMessage` and
* `port.onMessage.addListener` crashing your Vue3 UI with bullshits
* excuses, e.g.
*
* | TypeError: Illegal invocation. Function must be called on
* | an object of type Port
*
* which is some grade A bullshit because Firefox can handle that just
* fine.
*
* There's two ways how I could handle this:
* * Find out how to get the original object from the proxy Vue3
* creates, which would take time and ruin my xmass holiday, or
* * make a global object with a passive-aggressive name and ignore
* the very real possibility that there's prolly a reason Chrome
* does things in its own very special(tm) way, as if it had one
* extra chromosome over Firefox.
*
* Easy choice, really.
*/
export class ChromeShittinessMitigations {
static port = null;
static setProperty(property, value) {
ChromeShittinessMitigations[property] = value;
}
}
export default ChromeShittinessMitigations;

24
src/common/js/IO.ts Normal file
View File

@ -0,0 +1,24 @@
class IO {
/**
* Export a (presumably json) string to file. Meant for use with content script.
* @param {*} jsonString string to be saved
*/
static async csStringToFile(jsonString) {
console.info("\n\n\n\n---------- Starting export of log to file ----------------");
console.info("[info] json string for exportObject:", jsonString.length);
const blob = new Blob([jsonString], {type: 'application/json'});
const fileUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = fileUrl;
a.download = 'ultrawidify-log.log';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(fileUrl);
}
}
export default IO;

View File

@ -1,6 +1,6 @@
class KeyboardShortcutParser { class KeyboardShortcutParser {
static parseShortcut(keypress) { static parseShortcut(keypress) {
var shortcutCombo = ''; let shortcutCombo = '';
if (keypress.ctrlKey) { if (keypress.ctrlKey) {
shortcutCombo += 'Ctrl + '; shortcutCombo += 'Ctrl + ';
@ -21,6 +21,20 @@ class KeyboardShortcutParser {
} }
return shortcutCombo; return shortcutCombo;
} }
static generateShortcutFromKeypress(event) {
return {
ctrlKey: event.ctrlKey,
altKey: event.altKey,
shiftKey: event.shiftKey,
metaKey: event.metaKey,
code: event.code,
key: event.key,
keyup: true,
keydown: false,
type: event.type, // only needed for purposes of EditShortcutButton
}
}
} }
export default KeyboardShortcutParser; export default KeyboardShortcutParser;

View File

@ -1,17 +0,0 @@
<template>
<div>
<p><i><a href="https://www.youtube.com/watch?v=Mn3YEJTSYs8&feature=youtu.be&t=770" target='_blank'>...</a> but as is normal, you can't use a free extensions without the developers begging for money. It's the bi-daily beggathon here on ... wherever here is.</i>
</p>
<p>
Jokes and references few will get aside, developing this extension does take a decent amount of time, motivation, carefully calibrated quantities
of alcohol and enough coffee to bankrupt a small nation.
</p>
<p>If you want to buy me a beer or bankroll my caffeine addiction, you can do so by <a href="https://paypal.me/tamius">clicking here</a>. All donations are appreciated.
</p>
</div>
</template>
<script>
</script>

View File

@ -1,28 +0,0 @@
export default {
computed: {
scopeActions: function() {
return this.settings.active.actions.filter(x => x.scopes[this.scope] && x.scopes[this.scope].show) || [];
},
extensionActions: function(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-extension-mode') || [];
},
aardActions: function(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-autoar-mode') || [];
},
aspectRatioActions: function(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-ar') || [];
},
stretchActions: function(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-stretch') || [];
},
keyboardActions: function() {
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-keyboard') || [];
},
alignmentActions: function() {
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-alignment') || [];
},
otherActions: function() {
return this.scopeActions.filter(x => x.cmd.length > 1) || [];
}
}
}

View File

@ -1,12 +1,12 @@
import Debug from '../conf/Debug'; import Debug from '../../ext/conf/Debug';
class ObjectCopy { class ObjectCopy {
static addNew(existing, target){ static addNew(current, newValues){
// clone target // clone target
var out = JSON.parse(JSON.stringify(target)); let out = JSON.parse(JSON.stringify(newValues));
if(! existing) { if(! current) {
if(Debug.debug) { if(Debug.debug) {
console.log("[ObjectCopy::addNew] There's no existing value. Returning target value."); console.log("[ObjectCopy::addNew] There's no existing value. Returning target value.");
} }
@ -14,12 +14,12 @@ class ObjectCopy {
return out; return out;
} }
for(var k in out) { for(let k in out) {
// if current key exist, replace it with existing value. Take no action otherwise. // if current key exist, replace it with existing value. Take no action otherwise.
if(existing[k]) { if(current[k]) {
// Types and constructors of objects must match. If they don't, we always use the new value. // Types and constructors of objects must match. If they don't, we always use the new value.
if(typeof out[k] === typeof existing[k] && out[k].constructor === existing[k].constructor) { if(typeof out[k] === typeof current[k] && out[k].constructor === current[k].constructor) {
// objects are special, we need to check them recursively. // objects are special, we need to check them recursively.
if(out[k] && typeof out[k] === 'object' && out[k].constructor === Object ) { if(out[k] && typeof out[k] === 'object' && out[k].constructor === Object ) {
@ -27,9 +27,9 @@ class ObjectCopy {
console.log("[ObjectCopy::addNew] current key contains an object. Recursing!") console.log("[ObjectCopy::addNew] current key contains an object. Recursing!")
} }
out[k] = this.addNew(existing[k], out[k]); out[k] = this.addNew(current[k], out[k]);
} else { } else {
out[k] = existing[k]; out[k] = current[k];
} }
} }
} }
@ -37,39 +37,41 @@ class ObjectCopy {
// add the values that would otherwise be deleted back to our object. (We need that so user-defined // add the values that would otherwise be deleted back to our object. (We need that so user-defined
// sites don't get forgotten) // sites don't get forgotten)
for(var k in existing) { for(let k in current) {
if (! out[k]) { if (! out[k]) {
out[k] = existing[k]; out[k] = current[k];
} }
} }
return out; return out;
} }
static overwrite(existing, target){ static overwrite(current, newValues){
for(var k in target) { for(let k in newValues) {
// if current key exist, replace it with existing value. Take no action otherwise. // if current key exist, replace it with existing value. Take no action otherwise.
if (existing[k]) { if (current[k] !== undefined) {
// Types and constructors of objects must match. If they don't, we always use the new value. // Types and constructors of objects must match. If they don't, we always use the new value.
if (typeof target[k] === typeof existing[k] && target[k].constructor === existing[k].constructor) { if (typeof newValues[k] === typeof current[k] && newValues[k].constructor === current[k].constructor) {
// objects are special, we need to check them recursively. // objects are special, we need to check them recursively.
if(existing[k] && typeof existing[k] === 'object' && existing[k].constructor === Object ) { if(current[k] && typeof current[k] === 'object' && current[k].constructor === Object ) {
if(Debug.debug && Debug.settings) { if(Debug.debug && Debug.settings) {
console.log("[ObjectCopy::addNew] current key contains an object. Recursing!") console.log("[ObjectCopy::addNew] current key contains an object. Recursing!")
} }
existing[k] = this.overwrite(existing[k], target[k]); current[k] = this.overwrite(current[k], newValues[k]);
} else { } else {
existing[k] = target[k]; current[k] = newValues[k];
} }
} else { } else {
existing[k] = target[k]; current[k] = newValues[k];
} }
} }
else if (newValues[k] !== undefined) {
current[k] = newValues[k];
}
} }
return existing; return current;
} }
static pruneUnused(existing, target, ignoreKeys) { static pruneUnused(existing, target, ignoreKeys) {

14
src/common/utils/_cp.ts Normal file
View File

@ -0,0 +1,14 @@
/**
* Creates deep copy of an object
* @param obj
* @returns
*/
export function _cp(obj) {
try {
return JSON.parse(JSON.stringify(obj));
} catch (e) {
// console.error('Failed to parse json. This probably means that the data we received was not an object. Will return data as-is');
// console.error('data in:', obj, 'error:', e);
return obj;
}
}

View File

@ -0,0 +1,12 @@
export function collectionHas(collection, element): boolean {
for (let i = 0, len = collection.length; i < len; i++) {
if (collection[i] == element) {
return true;
}
}
return false;
}
export function equalish(a: number,b: number, tolerance: number): boolean {
return a > b - tolerance && a < b + tolerance;
}

View File

@ -0,0 +1,103 @@
export type ProcessedElementStyles = {
[x: string]: {
css: string,
pxValue: number
}
};
/**
* Note that this function is written _very_ dangerously
* and includes absolutely no error handling
*/
function getPixelValue(value: string, element?: HTMLElement, prop?: string) {
if (value === undefined || value === null) {
return null;
}
if (value.endsWith('px')) {
// console.log('value ends in px:', value)
return parseFloat(value);
}
if (value.endsWith('%')) {
// console.log('value ends in %:', value)
// This allegedly doesn't work for certain types of properties, allegedly.
const parent = element?.parentElement;
if (parent && prop) {
const parentDimensions = parent.getBoundingClientRect();
return (parseFloat(value) * 0.01) * (prop.includes('height') || ['top', 'bottom'].includes(prop) ? parentDimensions.height : parentDimensions.width);
} else {
return null;
}
}
if (value.endsWith("vw")) {
const viewportWidth = window.innerWidth;
return (parseFloat(value) / 100) * viewportWidth;
}
if (value.endsWith("vh")) {
const viewportHeight = window.innerHeight;
return (parseFloat(value) / 100) * viewportHeight;
}
if (value.endsWith("rem")) {
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
return rootFontSize * parseFloat(value);
}
if (value.endsWith("em")) {
const fontSize = parseFloat(getComputedStyle(element).fontSize);
return fontSize * parseFloat(value);
}
};
export default function getElementStyles(element: HTMLElement, props: string[], theoryProps?: string[]): ProcessedElementStyles {
const stylesheets = document.styleSheets;
const computedStyles = getComputedStyle(element);
const stylesOut = {};
for (const stylesheet of stylesheets) {
// console.log('——————————————— processing stylesheet:', stylesheet);
try {
for (const rule of stylesheet?.cssRules) {
if (rule instanceof CSSStyleRule) {
if (element.matches(rule.selectorText)) {
// console.log('element matches rule:', rule);
for (const property in rule.style) {
if (!props.includes(property)) {
continue;
}
const cssValue = rule.style.getPropertyValue(property);
if (theoryProps?.includes(property)) {
stylesOut[property] = {css: cssValue};
}
const actualValue = computedStyles.getPropertyValue(property);
const theory = getPixelValue(cssValue, element, property);
const practice = getPixelValue(actualValue, element, property);
if (theory && practice && (Math.abs(theory - practice) < 1)) {
stylesOut[property] = {
css: cssValue,
pxValue: theory
}
}
}
}
}
}
} catch (e) {
// Cross-origin styles amy cause problems
}
}
return stylesOut;
}

View File

@ -0,0 +1,49 @@
/**
* Reqzests iframe parent hostname
* @returns
*/
export async function getIframeParentHost(): Promise<string> {
return new Promise<any>((resolve) => {
let resolved = false;
let resendInterval;
function handleMessage(event) {
if (event.data.action === 'uw-parent-hostname') {
resolved = true;
clearInterval(resendInterval);
window.removeEventListener('message', handleMessage);
resolve(event.data.hostname);
}
}
window.addEventListener('message', handleMessage);
resendInterval = setInterval(
() => {
if (!resolved) {
window.parent.postMessage(
{ action: 'uw-get-parent-hostname' },
'*'
);
}
},
500
);
});
}
function handleMessage(event) {
if (event.data.action === 'uw-get-parent-hostname') {
event.source.postMessage(
{action: 'uw-parent-hostname', hostname: window.location.hostname},
'*' as any
)
}
}
export async function setupHostnameReporting() {
window.removeEventListener('message', handleMessage); // setupHostnameReporting may run more than once
window.addEventListener('message', handleMessage);
}

View File

@ -0,0 +1,3 @@
export async function sleep(timeout) {
return new Promise( (resolve, reject) => setTimeout(() => resolve(null), timeout));
}

145
src/ext/UWContent.ts Normal file
View File

@ -0,0 +1,145 @@
import Debug from '@src/ext/conf/Debug';
import CommsClient, { CommsOrigin } from '@src/ext/module/comms/CommsClient';
import EventBus from '@src/ext/module/EventBus';
import KeyboardHandler from '@src/ext/module/kbm/KeyboardHandler';
import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import { BLANK_LOGGER_CONFIG, LogAggregator } from '@src/ext/module/logging/LogAggregator';
import Settings from '@src/ext/module/settings/Settings';
import { SiteSettings } from '@src/ext/module/settings/SiteSettings';
import UI from '@src/ext/module/uwui/UI';
import PageInfo from '@src/ext/module/video-data/PageInfo';
import { getIframeParentHost, setupHostnameReporting } from '@src/common/utils/getHost';
export default class UWContent {
pageInfo: PageInfo;
comms: CommsClient;
settings: Settings;
siteSettings: SiteSettings;
keyboardHandler: KeyboardHandler;
logAggregator: LogAggregator;
logger: ComponentLogger;
eventBus: EventBus;
isIframe: boolean = false;
parentHostname: string;
globalUi: any;
commsHandlers: {
[x: string]: ((a: any, b?: any) => void | Promise<void>)[]
} = {
}
constructor(){
setupHostnameReporting();
this.isIframe = window.self !== window.top;
}
reloadSettings() {
try {
this.logger.debug('reloadSettings', 'Things happened in the popup. Will reload extension settings.');
this.init();
} catch (e) {
console.warn('Ultrawidify: settings reload failed. This probably shouldn\'t outright kill the extension, but page reload is recommended.');
}
}
async init(){
if (this.isIframe) {
this.parentHostname = await getIframeParentHost();
console.warn('[uw-content] got iframe parent:', this.parentHostname);
}
try {
if (Debug.debug) {
console.log("[uw::main] loading configuration ...");
}
// logger init is the first thing that needs to run
try {
if (!this.logger) {
this.logAggregator = new LogAggregator('◈');
this.logger = new ComponentLogger(this.logAggregator, 'UWContent');
await this.logAggregator.init(BLANK_LOGGER_CONFIG);
}
} catch (e) {
console.error("logger init failed!", e)
}
// init() is re-run any time settings change
if (this.comms) {
this.comms.destroy();
}
if (this.eventBus) {
this.eventBus.destroy();
}
if (!this.settings) {
this.settings = new Settings({
onSettingsChanged: () => this.reloadSettings(),
logAggregator: this.logAggregator
});
await this.settings.init();
this.siteSettings = this.settings.getSiteSettings({site: window.location.hostname, isIframe: this.isIframe, parentHostname: this.parentHostname});
}
this.eventBus = new EventBus({name: 'content-script', commsOrigin: CommsOrigin.ContentScript});
this.eventBus.subscribe(
'uw-restart',
{
source: this,
function: () => this.initPhase2()
}
);
this.comms = new CommsClient('content-main-port', this.logAggregator, this.eventBus);
this.eventBus.setComms(this.comms);
this.initPhase2();
} catch (e) {
console.error('Ultrawidify initialization failed for some reason:', e);
}
}
// we always initialize extension, even if it's disabled.
initPhase2() {
try {
if (this.pageInfo) {
this.logger.info('setup', 'An instance of pageInfo already exists and will be destroyed.');
this.pageInfo.destroy();
}
this.pageInfo = new PageInfo(this.eventBus, this.siteSettings, this.settings, this.logAggregator);
this.logger.debug('setup', "pageInfo initialized.");
this.logger.debug('setup', "will try to initiate KeyboardHandler.");
if (this.keyboardHandler) {
this.keyboardHandler.destroy();
}
this.keyboardHandler = new KeyboardHandler(this.eventBus, this.siteSettings, this.settings, this.logAggregator);
this.keyboardHandler.init();
this.logger.debug('setup', "KeyboardHandler initiated.");
if (this.globalUi) {
this.globalUi.destroy();
}
// this.globalUi = new UI('ultrawidify-global-ui', {eventBus: this.eventBus, isGlobal: true});
// this.globalUi.enable();
// this.globalUi.setUiVisibility(false);
} catch (e) {
console.error('Ultrawidify: failed to start extension. Error:', e)
this.logger.error('setup', "FAILED TO START EXTENSION. Error:", e);
}
}
destroy() {
this.eventBus.unsubscribeAll(this);
if (this.pageInfo) {
this.pageInfo.destroy();
}
if (this.keyboardHandler) {
this.keyboardHandler.destroy();
}
}
}

437
src/ext/UWServer.ts Normal file
View File

@ -0,0 +1,437 @@
import { Runtime } from 'chrome';
import EventBus from '@src/ext/module/EventBus';
import Settings from '@src/ext/module/settings/Settings';
import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import { BLANK_LOGGER_CONFIG, LogAggregator } from '@src/ext/module/logging/LogAggregator';
import CommsServer from '@src/ext/module/comms/CommsServer';
import BrowserDetect from '@src/ext/conf/BrowserDetect';
import { HostInfo } from '@src/common/interfaces/HostData.interface';
import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
import { CommsOrigin } from '@src/ext/module/comms/CommsClient';
const BASE_LOGGING_STYLES = {
'log': 'background-color: #243; color: #4a8',
}
export default class UWServer {
settings: Settings;
logger: ComponentLogger;
logAggregator: LogAggregator;
comms: CommsServer;
eventBus: EventBus;
ports: any[] = [];
hasVideos: boolean;
currentSite: string = '';
videoTabs: any = {};
currentTabId: number = 0;
selectedSubitem: any = {
'siteSettings': undefined,
'videoSettings': undefined,
}
eventBusCommands = {
'popup-set-selected-tab': {
function: (message) => this.setSelectedTab(message.selectedMenu, message.selectedSubitem)
},
'has-video': {
function: (message, context) => this.registerVideo(context.comms.sender)
},
'noVideo' : {
function: (message, context) => this.unregisterVideo(context.comms.sender)
},
'inject-css': {
function: (message, context) => this.injectCss(message.cssString, context.comms.sender)
},
'eject-css': {
function: (message, context) => this.removeCss(message.cssString, context.comms.sender)
},
'replace-css': {
function: (message, context) => this.replaceCss(message.oldCssString, message.newCssString, context.comms.sender)
},
'get-current-site': {
function: (message, context) => this.getCurrentSite(context.comms.sender)
}
};
private gcTimeout: any;
uiLoggerInitialized: boolean = false;
//#region getters
get activeTab() {
return chrome.tabs.query({currentWindow: true, active: true});
}
//#endregion
constructor() {
this.setup();
}
async setup() {
try {
// logger is the first thing that goes up
const loggingOptions = BLANK_LOGGER_CONFIG;
this.logAggregator = new LogAggregator('🔶bg-script🔶');
this.logger = new ComponentLogger(this.logAggregator, 'UwServer', {styles: BASE_LOGGING_STYLES});
await this.logAggregator.init(loggingOptions);
this.settings = new Settings({logAggregator: this.logAggregator});
await this.settings.init();
this.eventBus = new EventBus({isUWServer: true, commsOrigin: CommsOrigin.Server});
this.eventBus.subscribeMulti(this.eventBusCommands, this);
this.comms = new CommsServer(this);
this.eventBus.setComms(this.comms);
chrome.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)});
} catch (e) {
console.error(`Ultrawidify [server]: failed to start. Reason:`, e);
}
}
async _promisifyTabsGet(browserObj, tabId){
return new Promise( (resolve, reject) => {
browserObj.tabs.get(tabId, (tab) => resolve(tab));
});
}
//#region CSS management
async injectCss(css, sender) {
if (!sender?.tab?.id) {
// console.warn('invalid injectCss received!');
return;
}
this.logger.info('injectCss', 'Trying to inject CSS into tab', sender.tab.id, ', frameId:', sender.frameId, 'css:\n', css)
if (!css) {
return;
}
try {
await chrome.scripting.insertCSS({
target: {
tabId: sender.tab.id,
frameIds: [
sender.frameId
]
},
css,
origin: "USER"
});
} catch (e) {
this.logger.error('injectCss', 'Error while injecting css:', {error: e, css, sender});
}
}
async removeCss(css, sender) {
if (!sender?.tab) {
// console.warn('invalid removeCss received!');
return;
}
try {
await chrome.scripting.removeCSS({
target: {
tabId: sender.tab.id,
frameIds: [
sender.frameId
]
},
css,
origin: "USER"
});
} catch (e) {
this.logger.error('injectCss', 'Error while removing css:', {error: e, css, sender});
}
}
async replaceCss(oldCss, newCss, sender) {
if (!sender?.tab) {
// console.warn('invalid replaceCss received!');
return;
}
if (oldCss !== newCss) {
this.removeCss(oldCss, sender);
this.injectCss(newCss, sender);
}
}
//#endregion
extractHostname(url){
var hostname;
if (!url) {
return "<no url>";
}
// extract hostname
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
hostname = url.split('/')[2];
}
else {
hostname = url.split('/')[0];
}
hostname = hostname.split(':')[0]; //find & remove port number
hostname = hostname.split('?')[0]; //find & remove "?"
return hostname;
}
async onTabSwitched(activeInfo){
this.hasVideos = false;
try {
this.currentTabId = activeInfo.tabId; // just for readability
let tab;
if (BrowserDetect.firefox) {
tab = await chrome.tabs.get(this.currentTabId);
} else if (BrowserDetect.anyChromium) {
tab = await this._promisifyTabsGet(chrome, this.currentTabId);
}
this.currentSite = this.extractHostname(tab.url);
this.logger.info('onTabSwitched', 'user switched tab. New site:', this.currentSite);
} catch(e) {
this.logger.info('onTabSwitched', 'there was a problem getting current site:', e)
}
this.selectedSubitem = {
'siteSettings': undefined,
'videoSettings': undefined,
}
//TODO: change extension icon based on whether there's any videos on current page
}
registerVideo(sender) {
if (!sender?.tab?.url) {
// console.warn('invalid registerVideo received!');
return;
}
this.logger.info('registerVideo', 'Registering video.\nsender:', sender);
const tabHostname = this.extractHostname(sender.tab.url);
const frameHostname = this.extractHostname(sender.url);
// check for orphaned/outdated values and remove them if necessary
if (this.videoTabs[sender.tab.id]?.host != tabHostname) {
delete this.videoTabs[sender.tab.id]
} else if(this.videoTabs[sender.tab.id]?.frames[sender.frameId]?.host != frameHostname) {
delete this.videoTabs[sender.tab.id].frames[sender.frameId];
}
if (this.videoTabs[sender.tab.id]) {
this.videoTabs[sender.tab.id].frames[sender.frameId] = {
id: sender.frameId,
host: frameHostname,
url: sender.url,
registerTime: Date.now(),
}
} else {
this.videoTabs[sender.tab.id] = {
id: sender.tab.id,
host: tabHostname,
url: sender.tab.url,
frames: {}
};
this.videoTabs[sender.tab.id].frames[sender.frameId] = {
id: sender.frameId,
host: frameHostname,
url: sender.url,
registerTime: Date.now(),
}
}
this.logger.info('registerVideo', 'Video registered. current videoTabs:', this.videoTabs);
}
unregisterVideo(sender) {
if (!sender?.tab) {
// console.warn('invalid unregisterVideo received!');
return;
}
this.logger.info('unregisterVideo', 'Unregistering video.\nsender:', sender);
if (this.videoTabs[sender.tab.id]) {
if ( Object.keys(this.videoTabs[sender.tab.id].frames).length <= 1) {
delete this.videoTabs[sender.tab.id]
} else {
if(this.videoTabs[sender.tab.id].frames[sender.frameId]) {
delete this.videoTabs[sender.tab.id].frames[sender.frameId];
}
}
}
this.logger.info('unregisterVideo', 'Video has been unregistered. Current videoTabs:', this.videoTabs);
}
setSelectedTab(menu, subitem) {
this.logger.info('setSelectedTab', 'saving selected tab for', menu, ':', subitem);
this.selectedSubitem[menu] = subitem;
}
async getCurrentSite(sender: Runtime.MessageSender) {
// if (!sender?.tab) {
// console.warn('invalid unregisterVideo received!');
// return;
// }
const site = await this.getVideoTab();
// Don't propagate 'INVALID SITE' to the popup.
if (site.host === 'INVALID SITE') {
this.logger.info('getCurrentSite', 'Host is not valid — no info for current tab.');
return;
}
const tabHostname = await this.getCurrentTabHostname();
this.logger.info('getCurrentSite', 'Returning data:', {site, tabHostname});
console.info('get-current-site : returning data:', {site, tabHostname});
this.eventBus.send(
'set-current-site',
{
site,
tabHostname,
},
{
comms: {
forwardTo: 'popup'
}
}
)
}
async getCurrentTab() {
return (await chrome.tabs.query({active: true, currentWindow: true}))[0];
}
private populateFrameVideoStatus(tabId: number, hostnames: string[]) {
const out: HostInfo[] = [];
for (const host of hostnames) {
let video = {hasVideo: false, minEnvironment: ExtensionEnvironment.Normal, maxEnvironment: ExtensionEnvironment.Fullscreen};
for (const frameKey in this.videoTabs[tabId].frames) {
const frame = this.videoTabs[tabId].frames[frameKey];
if (frame.host === host) {
video.hasVideo = true;
if (frame.environment > video.maxEnvironment) {
video.maxEnvironment = frame.environment;
}
if (frame.environment < video.minEnvironment) {
video.minEnvironment = frame.environment;
}
}
}
out.push({
host,
...video,
})
}
return out;
}
private _lastVideoTabData: any | undefined;
async getVideoTab() {
// friendly reminder: if current tab doesn't have a video,
// there won't be anything in this.videoTabs[this.currentTabId]
const ctab = await this.getCurrentTab();
if (!ctab || !ctab.id) {
return {
host: 'INVALID SITE',
frames: [],
hostnames: [],
}
}
const hostnames = await this.comms.listUniqueFrameHosts();
// this probably means we're inside a problematic page
if (!this.videoTabs[ctab.id]) {
return this._lastVideoTabData ?? {
host: 'INVALID SITE',
frames: [],
hostnames: [],
}
}
// if video is older than PageInfo's video rescan period (+ 4000ms of grace),
// we clean it up from videoTabs[tabId].frames array.
const ageLimit = Date.now() - this.settings.active.pageInfo.timeouts.rescan - 4000;
try {
for (const key in this.videoTabs[ctab.id].frames) {
if (this.videoTabs[ctab.id].frames[key].registerTime < ageLimit) {
delete this.videoTabs[ctab.id].frames[key];
}
}
} catch (e) {
// something went wrong. There's prolly no frames.
// this probably shouldn't ever run.
return {
host: this.extractHostname(ctab.url),
hostnames: [],
frames: [],
selected: this.selectedSubitem
}
}
// Ensure hostnames with videos come before hostnames without videos
// Also ensure hostnames are sorted alphabetically
const populatedHostnames = this.populateFrameVideoStatus(ctab.id, hostnames);
populatedHostnames.sort((a: HostInfo, b: HostInfo) => {
return a.hasVideo === b.hasVideo
? a.host === b.host ? 0 : a.host < b.host ? -1 : 1
: a.hasVideo < b.hasVideo ? 1 : -1;
});
this._lastVideoTabData = {
host: this.extractHostname(ctab.url),
hostnames: populatedHostnames.map(x => x.host), // todo: try eliminating this
populatedHostnames: populatedHostnames,
selected: this.selectedSubitem
};
return this._lastVideoTabData;
}
async getCurrentTabHostname() {
const activeTab = await this.activeTab;
if (!activeTab || activeTab.length < 1) {
return null;
}
const url = activeTab[0].url;
if (!url) {
console.log('no URL for active tab:', activeTab[0].url);
}
var hostname;
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
hostname = url.split('/')[2];
}
else {
hostname = url.split('/')[0];
}
hostname = hostname.split(':')[0]; //find & remove port number
hostname = hostname.split('?')[0]; //find & remove "?"
return hostname;
}
}

View File

@ -1,28 +1,37 @@
import VideoAlignment from '../../common/enums/video-alignment.enum'; import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
import Stretch from '../../common/enums/stretch.enum'; import StretchType from '../../common/enums/StretchType.enum';
import ExtensionMode from '../../common/enums/extension-mode.enum'; import ExtensionMode from '../../common/enums/ExtensionMode.enum';
import AspectRatio from '../../common/enums/aspect-ratio.enum'; import AspectRatioType from '../../common/enums/AspectRatioType.enum';
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
var ActionList = { var ActionList = {
'set-ar': { 'set-ar': {
name: 'Set aspect ratio', name: 'Set aspect ratio',
args: [{ args: [{
name: 'Automatic', name: 'Automatic',
arg: AspectRatio.Automatic, arg: AspectRatioType.Automatic,
},{ },{
name: 'Fit width', name: 'Fit width',
arg: AspectRatio.FitWidth, arg: AspectRatioType.FitWidth,
},{ },{
name: 'Fit height', name: 'Fit height',
arg: AspectRatio.FitHeight, arg: AspectRatioType.FitHeight,
},{ },{
name: 'Reset', name: 'Reset',
arg: AspectRatio.Reset, arg: AspectRatioType.Reset,
},{ },{
name: 'Manually specify ratio', name: 'Manually specify ratio',
arg: AspectRatio.Fixed, arg: AspectRatioType.Fixed,
customArg: true, customArg: true,
hintHTML: '', customSetter: (value) => {
const [width, height] = value.split(':');
if (width && height) {
return +width / +height;
}
return +width;
},
hintHTML: '<small>Enter the aspect ratio as {width}:{height} or a single number, e.g. "21:9", "2.35:1", or "2.35" (without quotes).</small>',
}], }],
scopes: { scopes: {
global: false, global: false,
@ -30,23 +39,64 @@ var ActionList = {
page: true, page: true,
} }
}, },
'set-ar-persistence': {
name: 'Set crop mode persistence',
args: [{
name: 'Never persist',
arg: CropModePersistence.Disabled,
},{
name: 'While on page',
arg: CropModePersistence.UntilPageReload,
},{
name: 'Current session',
arg: CropModePersistence.CurrentSession,
},{
name: 'Always persist',
arg: CropModePersistence.Forever,
}, {
name: 'Default',
arg: CropModePersistence.Default,
scopes: {
site: true,
}
}],
scopes: {
global: true,
site: true,
page: false,
}
},
'set-stretch': { 'set-stretch': {
name: 'Set stretch', name: 'Set stretch',
args: [{ args: [{
name: 'Normal', name: 'Normal',
arg: Stretch.NoStretch arg: StretchType.NoStretch
},{ },{
name: 'Basic', name: 'Basic',
arg: Stretch.Basic, arg: StretchType.Basic,
},{ },{
name: 'Hybrid', name: 'Hybrid',
arg: Stretch.Hybrid, arg: StretchType.Hybrid,
},{ },{
name: 'Thin borders', name: 'Thin borders',
arg: Stretch.Conditional, arg: StretchType.Conditional,
},{
name: 'Fixed (source)',
arg: StretchType.FixedSource,
customArg: true,
scopes: {
page: true,
}
},{
name: 'Fixed (displayed)',
arg: StretchType.Fixed,
customArg: true,
scopes: {
page: true,
}
},{ },{
name: 'Default', name: 'Default',
arg: Stretch.Default, arg: StretchType.Default,
scopes: { scopes: {
site: true site: true
} }
@ -61,16 +111,16 @@ var ActionList = {
name: 'Set video alignment', name: 'Set video alignment',
args: [{ args: [{
name: 'Left', name: 'Left',
arg: VideoAlignment.Left, arg: VideoAlignmentType.Left,
},{ },{
name: 'Center', name: 'Center',
arg: VideoAlignment.Center, arg: VideoAlignmentType.Center,
},{ },{
name: 'Right', name: 'Right',
arg: VideoAlignment.Right arg: VideoAlignmentType.Right
},{ },{
name: 'Default', name: 'Default',
arg: VideoAlignment.Default, arg: VideoAlignmentType.Default,
scopes: { scopes: {
site: true, site: true,
} }

View File

@ -1,13 +1,44 @@
import Debug from './Debug.js'; import browser from "vuex-webextensions/dist/browser";
if (process.env.CHANNEL !== 'stable') {
console.info('Loaded BrowserDetect');
}
function detectEdgeUA() {
try {
return /Edg\/(\.?[0-9]*)*$/.test(window.navigator.userAgent);
} catch {
return undefined;
}
}
function getBrowserObj() {
return process.env.BROWSER === 'firefox' ? browser : chrome;
}
function getRuntime() {
return process.env.BROWSER === 'firefox' ? browser.runtime : chrome.runtime;
}
function getURL(url) {
return process.env.BROWSER === 'firefox' ? browser.runtime.getURL(url) : chrome.runtime.getURL(url);
}
const BrowserDetect = { const BrowserDetect = {
firefox: process.env.BROWSER === 'firefox', firefox: process.env.BROWSER === 'firefox',
anyChromium: process.env.BROWSER !== 'firefox',
chrome: process.env.BROWSER === 'chrome', chrome: process.env.BROWSER === 'chrome',
edge: process.env.BROWSER === 'edge', edge: process.env.BROWSER === 'edge',
processEnvBrowser: process.env.BROWSER,
processEnvChannel: process.env.CHANNEL,
isEdgeUA: detectEdgeUA(),
browserObj: getBrowserObj(),
runtime: getRuntime(),
getURL: (url) => getURL(url),
} }
if (Debug.debug) { if (process.env.CHANNEL !== 'stable') {
console.log("Loading: BrowserDetect.js\n\nprocess.env.BROWSER:", process.env.BROWSER, "Exporting BrowserDetect:", BrowserDetect); console.info("BrowserDetect loaded:\n\nprocess.env.BROWSER:", process.env.BROWSER, "\nExporting BrowserDetect:", BrowserDetect);
} }
export default BrowserDetect; export default BrowserDetect;

View File

@ -1,3 +1,8 @@
if (process.env.CHANNEL !== 'stable') {
console.info('We are not on stable channel. File init will be printed to console.');
console.info('Loading Debug.js');
}
// Set prod to true when releasing // Set prod to true when releasing
// const _prod = true; // const _prod = true;
const _prod = false; const _prod = false;
@ -49,9 +54,13 @@ function __disableAllDebug(obj) {
} }
} }
if(Debug.debug) if (Debug.debug) {
console.log("Guess we're debugging ultrawidify then. Debug.js must always load first, and others must follow.\nLoading: Debug.js"); console.info("Guess we're debugging ultrawidify then. Debug.js must always load first, and others must follow.\nLoading: Debug.js");
}
if (process.env.CHANNEL !== 'stable') {
console.info('Loaded Debug.js');
}
export default Debug; export default Debug;

View File

@ -1,108 +0,0 @@
// How to use:
// version: {ExtensionConf object, but only properties that get overwritten}
const ExtensionConfPatch = [
{
forVersion: '4.2.0',
sites: {
"old.reddit.com" : {
type: 'testing',
DOM: {
player: {
manual: true,
useRelativeAncestor: false,
querySelectors: '.reddit-video-player-root, .media-preview-content'
}
},
css: '',
},
"www.reddit.com" : {
type: 'testing',
DOM: {
player: {
manual: true,
useRelativeAncestor: false,
querySelectors: '.reddit-video-player-root, .media-preview-content'
}
},
css: '',
},
"www.youtube.com" : {
DOM: {
player: {
manual: true,
querySelectors: "#movie_player, #player",
additionalCss: "",
useRelativeAncestor: false,
playerNodeCss: "",
}
}
},
}
}, {
forVersion: '4.2.3.1',
sites: {
"old.reddit.com" : {
type: 'testing',
DOM: {
player: {
manual: true,
useRelativeAncestor: false,
querySelectors: '.media-preview-content, .reddit-video-player-root'
}
},
css: '',
},
"www.reddit.com" : {
type: 'testing',
DOM: {
player: {
manual: true,
useRelativeAncestor: false,
querySelectors: '.media-preview-content, .reddit-video-player-root'
}
},
css: '',
},
"www.youtube.com" : {
DOM: {
player: {
manual: true,
querySelectors: "#movie_player, #player",
additionalCss: "",
useRelativeAncestor: false,
playerNodeCss: "",
}
}
},
}
}, {
forVersion: '4.3.0',
sites: {
"old.reddit.com" : {
type: 'testing',
DOM: {
player: {
manual: false,
useRelativeAncestor: false,
querySelectors: '.reddit-video-player-root, .media-preview-content'
}
},
css: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
},
"www.reddit.com" : {
type: 'testing',
DOM: {
player: {
manual: false,
useRelativeAncestor: false,
querySelectors: '.reddit-video-player-root, .media-preview-content'
}
},
css: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
},
}
}
];
export default ExtensionConfPatch;

View File

@ -0,0 +1,539 @@
// How to use:
// version: {ExtensionConf object, but only properties that get overwritten}
import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
import CropModePersistence from '@src/common/enums/CropModePersistence.enum';
import EmbeddedContentSettingsOverridePolicy from '@src/common/enums/EmbeddedContentSettingsOverridePolicy.enum';
import ExtensionMode from '@src/common/enums/ExtensionMode.enum';
import { InputHandlingMode } from '@src/common/enums/InputHandlingMode.enum';
import LegacyExtensionMode from '@src/common/enums/LegacyExtensionMode.enum';
import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum';
import { SiteSupportLevel } from '@src/common/enums/SiteSupportLevel.enum';
import SettingsInterface from '@src/common/interfaces/SettingsInterface';
import { _cp } from '@src/common/utils/_cp';
import { update } from 'lodash';
const ExtensionConfPatch = Object.freeze([
{
forVersion: '6.2.4',
updateFn: (userOptions: any, defaultOptions, logger?) => {
for (const site in userOptions.sites) {
(userOptions as any).sites[site].enableUI = {
fullscreen: LegacyExtensionMode.Default,
theater: LegacyExtensionMode.Default,
normal: LegacyExtensionMode.Default,
}
}
userOptions.sites['@global'].enableUI = {
fullscreen: userOptions.ui.inPlayer.enabled ? LegacyExtensionMode.Enabled : LegacyExtensionMode.Disabled,
theater: LegacyExtensionMode.Enabled,
normal: (userOptions.ui.inPlayer.enabled && !userOptions.ui.inPlayer.enabledFullscreenOnly) ? LegacyExtensionMode.Enabled : LegacyExtensionMode.Disabled
}
userOptions.sites['@empty'].enableUI = {
fullscreen: LegacyExtensionMode.Default,
theater: LegacyExtensionMode.Default,
normal: LegacyExtensionMode.Default,
}
}
}, {
forVersion: '6.2.6',
updateFn: (userOptions: any, defaultOptions, logger?) => {
console.log('[ultrawidify] Migrating settings — applying patches for version 6.2.6');
if (!userOptions.commands) {
userOptions.commands = {
zoom: [],
crop: [],
stretch: [],
pan: [],
internal: []
};
}
userOptions.commands.zoom = [{
action: 'change-zoom',
label: 'Zoom +5%',
arguments: {
zoom: 0.05
},
shortcut: {
key: 'z',
code: 'KeyY',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: false,
onKeyUp: true,
onKeyDown: false,
},
internalOnly: true,
actionId: 'change-zoom-10in'
}, {
action: 'change-zoom',
label: 'Zoom -5%',
arguments: {
zoom: -0.05
},
shortcut: {
key: 'u',
code: 'KeyU',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: false,
onKeyUp: true,
onKeyDown: false,
},
internalOnly: true,
actionId: 'change-zoom-10out'
}, {
action: 'set-zoom',
label: 'Reset zoom',
arguments: {
zoom: 1,
},
internalOnly: true,
actionId: 'set-zoom-reset'
}];
delete (userOptions as any).actions;
userOptions.dev = {
loadFromSnapshot: false
};
userOptions.ui.dev = {
aardDebugOverlay: {
showOnStartup: false,
showDetectionDetails: true
}
}
const newZoomActions = [{
action: 'set-zoom',
label: 'Reset zoom',
shortcut: {
key: 'r',
code: 'KeyR',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: true,
onKeyUp: true,
onKeyDown: false,
},
arguments: {
zoom: 1
},
internalOnly: true,
actionId: 'set-zoom-reset'
}, {
action: 'set-ar-zoom',
label: 'Automatic',
comment: 'Automatically detect aspect ratio and zoom accordingly',
arguments: {
type: AspectRatioType.Automatic
},
shortcut: {
key: 'a',
code: 'KeyA',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: true,
onKeyUp: true,
onKeyDown: false,
}
}, {
action: 'set-ar-zoom',
label: 'Cycle',
comment: 'Cycle through zoom options',
arguments: {
type: AspectRatioType.Cycle
},
shortcut: {
key: 'c',
code: 'KeyC',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: true,
onKeyUp: true,
onKeyDown: false,
}
}, {
action: 'set-ar-zoom',
label: '21:9',
comment: 'Zoom for 21:9 aspect ratio (1:2.39)',
arguments: {
type: AspectRatioType.Fixed,
ratio: 2.39
},
shortcut: {
key: 'd',
code: 'KeyD',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: true,
onKeyUp: false,
onKeyDown: true,
}
}, {
action: 'set-ar-zoom',
label: '18:9',
comment: 'Zoom for 18:9 aspect ratio (1:2)',
arguments: {
type: AspectRatioType.Fixed,
ratio: 1.78
},
shortcut: {
key: 's',
code: 'KeyS',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: true,
onKeyUp: false,
onKeyDown: true,
}
}, {
action: 'set-ar-zoom',
label: '32:9',
comment: 'Zoom for 32:9 aspect ratio',
arguments: {
type: AspectRatioType.Fixed,
ratio: 3.56
},
}];
const compareShortcuts = (a: any, b: any) => {
if (!a || !b) {
return false;
}
return a.key === b.key && b.code === b.code && a.ctrlKey == b.ctrlKey && a.shiftKey == b.shiftKey && a.metaKey == a.metaKey && a.altKey == b.altKey;
}
const hasConflict = (shortcut: any) => {
for (const ct in userOptions.commands) {
for (const command of userOptions.commands[ct]) {
if (compareShortcuts(shortcut, command.shortcut)) {
return true;
}
}
}
return false;
}
for (const zoomAction of newZoomActions) {
if (
!userOptions.commands.zoom.find(
x => x.action === zoomAction.action
&& x.arguments?.type === zoomAction.arguments?.type
&& x.arguments?.ratio === zoomAction.arguments?.ratio
)
) {
userOptions.commands.zoom.push({
...zoomAction,
shortcut: hasConflict(zoomAction.shortcut) ? undefined : zoomAction.shortcut
});
}
}
}
}, {
forVersion: '6.3.92',
updateFn: (userOptions: any) => {
// applyToEmbeddedContent is now an enum, and also no longer optional
for (const site in userOptions.sites) {
if (userOptions.sites[site].applyToEmbeddedContent === undefined) {
userOptions.sites[site].applyToEmbeddedContent = EmbeddedContentSettingsOverridePolicy.UseAsDefault;
} else {
userOptions.sites[site].applyToEmbeddedContent = userOptions.sites[site].applyToEmbeddedContent ? EmbeddedContentSettingsOverridePolicy.Always : EmbeddedContentSettingsOverridePolicy.Never;
}
}
}
},
{
forVersion: '6.3.93',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
(userOptions as any).arDetect.polling = defaultOptions.aard.polling;
(userOptions as any).arDetect.subtitles = defaultOptions.aard.subtitles;
(userOptions as any).arDetect.autoDisable = defaultOptions.aard.autoDisable;
}
},
{
forVersion: '6.3.98',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
(userOptions as any).arDetect.letterboxOrientationScan = defaultOptions.aard.letterboxOrientationScan;
(userOptions as any).arDetect.edgeDetection = defaultOptions.aard.edgeDetection;
(userOptions as any).arDetect.subtitles = defaultOptions.aard.subtitles;
}
},
{
forVersion: '6.3.98',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
userOptions.aard = defaultOptions.aard;
userOptions.aardLegacy = defaultOptions.aardLegacy;
delete (userOptions as any).arDetect;
}
},
{
forVersion: '6.3.994',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface, logger?) => {
const convertLegacyExtensionMode = (option: {normal: LegacyExtensionMode, theater: LegacyExtensionMode, fullscreen: LegacyExtensionMode}) => {
if (typeof option === 'number') {
logger.warn('updateFn', 'This option is not an object, which suggests it has already been converted. Skipping conversion. Raw value of option:', option);
return option;
}
logger.log(
'updateFn',
`\nconverting option ——— normal: ${LegacyExtensionMode[option.normal]} theater: ${LegacyExtensionMode[option.normal]} fs: ${LegacyExtensionMode[option.fullscreen]}`, '\nraw obj:', option
);
if (option.normal === LegacyExtensionMode.Enabled) {
return ExtensionMode.All;
}
if (option.theater === LegacyExtensionMode.Enabled) {
return ExtensionMode.Theater;
}
if (option.fullscreen === LegacyExtensionMode.Enabled) {
return ExtensionMode.FullScreen;
}
if (option.fullscreen === LegacyExtensionMode.Disabled) {
return ExtensionMode.Disabled;
}
return ExtensionMode.Default;
}
for (const key in userOptions.sites) {
logger.log('updateFn', '\n\n ... migrating default enable-state for site', key);
userOptions.sites[key].enable = convertLegacyExtensionMode(userOptions.sites[key].enable as any);
userOptions.sites[key].enableAard = convertLegacyExtensionMode(userOptions.sites[key].enableAard as any);
if (key === '@global') {
userOptions.sites['@global'].enableKeyboard = userOptions.kbm.enabled && userOptions.kbm.keyboardEnabled ? InputHandlingMode.Enabled : InputHandlingMode.Disabled;
} else {
userOptions.sites[key].enableKeyboard = InputHandlingMode.Default;
}
userOptions.sites[key].enableUI = convertLegacyExtensionMode(
userOptions.sites[key].enableUI ?? (key === '@global' ? ExtensionMode.FullScreen : ExtensionMode.Default) as any
);
logger.log('updateFn', 'migrated site', key, userOptions.sites[key]);
}
}
},
{
forVersion: '6.3.995',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface, logger?) => {
if (!userOptions.ui) {
userOptions.ui = defaultOptions.ui
};
}
}, {
forVersion: '6.3.996',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface, logger?) => {
for (const site in userOptions.sites) {
const siteData = userOptions.sites[site];
logger.log('updateFn', 'migrating settings for', site, ' — persistCSA?', siteData.persistCSA, 'typeof persistCSA?', typeof siteData.persistCSA, 'does domconfig exist?', siteData.DOMConfig);
if (typeof siteData.persistCSA !== 'number') {
userOptions.sites[site].persistCSA = CropModePersistence.Default;
} else {
userOptions.sites[site].persistCSA = userOptions.sites[site].persistCSA ?? CropModePersistence.Default;
}
if (userOptions.sites[site].type as any === 'user-added' || userOptions.sites[site].type === 'user-defined') {
userOptions.sites[site].type = SiteSupportLevel.UserDefined;
}
if (userOptions.sites[site].type as any === 'no-support') {
userOptions.sites[site].type = SiteSupportLevel.Unknown;
}
if (userOptions.sites[site].defaultType as any === 'user-added' || userOptions.sites[site].defaultType === 'user-defined') {
userOptions.sites[site].type = SiteSupportLevel.UserDefined;
}
if (userOptions.sites[site].defaultType as any === 'no-support') {
userOptions.sites[site].defaultType = SiteSupportLevel.Unknown;
}
if (siteData.activeDOMConfig?.startsWith('community') || siteData.activeDOMConfig === 'official' || siteData.activeDOMConfig === 'empty' || siteData.activeDOMConfig === 'auto') {
siteData.activeDOMConfig = `@${siteData.activeDOMConfig}`;
}
for (const domConf in siteData.DOMConfig) {
logger.log('updateFn', "Updating domconf", domConf);
const oldConf = userOptions.sites[site].DOMConfig[domConf] as any;
logger.log('updateFn', "——— oldConf:", oldConf);
const newConf: any = {
type: oldConf.type ?? userOptions.sites[site].type,
elements: {}
};
if (newConf.type === 'user-added' || newConf.type === 'user-defined') {
newConf.type = SiteSupportLevel.UserDefined;
}
if (newConf.type === 'no-support') {
newConf.type = SiteSupportLevel.Unknown;
}
if (oldConf.elements?.player) {
newConf.elements['player'] = {
detectionMode: oldConf?.elements?.player?.manual ? (
oldConf?.elements?.player?.querySelectors.trim() ? PlayerDetectionMode.QuerySelectors : PlayerDetectionMode.AncestorIndex
) : PlayerDetectionMode.Auto,
allowAutoFallback: true,
ancestorIndex: oldConf?.elements?.player?.parentIndex,
querySelectors: oldConf?.elements?.player?.querySelectors,
}
} else {
newConf.elements['player'] = {
detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true,
}
}
if (oldConf.customCss) {
newConf.customCss = oldConf.customCss;
}
if (oldConf.elements?.video) {
newConf.elements['video'] = {
type: oldConf.type ?? siteData.type,
elements: {
video: {
playerDetectionMode: oldConf?.elements?.video?.manual ? PlayerDetectionMode.QuerySelectors : PlayerDetectionMode.Auto,
allowAutoFallback: true,
querySelectors: oldConf?.elements?.video?.querySelectors,
customCSS: oldConf?.elements?.video?.customCss,
}
}
}
}
// migrate names — official and community options get @ at the start
let domConfName = domConf;
if (domConfName.startsWith('community') || domConfName === 'official' || domConfName === 'empty' || domConfName === 'auto') {
domConfName = `@${domConfName}`;
}
if (domConfName !== domConf) {
logger.warn(`updateFn`, '\n\nnaming for default domConf has changed. Old:', domConf, 'new:', domConfName);
delete userOptions.sites[site].DOMConfig[domConf];
}
userOptions.sites[site].DOMConfig[domConfName] = newConf;
}
}
// set new defaults for global and empty:
userOptions.sites['@global'].DOMConfig = {
'@auto': {
type: SiteSupportLevel.Unknown,
elements: {
player: {
detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true,
},
video: {
detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true,
}
}
}
}
userOptions.sites['@global'].activeDOMConfig = '@auto';
userOptions.sites['@empty'].DOMConfig = {
'@empty': {
type: SiteSupportLevel.UserDefined,
elements: {
player: {
detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true,
},
video: {
detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true,
}
},
}
};
userOptions.sites['@empty'].activeDOMConfig = '@empty';
logger.log('updateFn', 'Migration complete. New site settings:', userOptions.sites);
}
}, {
forVersion: '6.3.997',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface, logger?) => {
// default zoom key combinations that involved the 'shift' key should have
// shortcut.key in uppercase, but they didn't.
for (const command of userOptions.commands?.zoom) {
if (command.shortcut) {
if (command.shortcut.shiftKey) {
if (command.shortcut.key.toUpperCase() === command.shortcut.code.charAt(3)) {
command.shortcut.key = command.shortcut.key.toUpperCase();
}
}
}
}
}
}, {
forVersion: '6.3.998',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface, logger?) => {
if (!userOptions.sites["www.amazon.com"]) {
userOptions.sites["www.amazon.com"] = _cp(defaultOptions.sites["www.amazon.com"] );
}
if (userOptions.commands?.zoom) {
const firstFixed = userOptions.commands?.zoom.findIndex(x => x.arguments.type === AspectRatioType.Fixed);
if (firstFixed === -1) {
userOptions.commands.zoom.push({
action: 'set-ar-zoom',
label: 'Cover',
comment: 'Covers the entire screen, cropping as much as needed',
arguments: {
type: AspectRatioType.Cover
},
shortcut: {
key: 'w',
code: 'KeyW',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: true,
onKeyUp: true,
onKeyDown: false,
}
});
} else {
userOptions.commands.zoom.splice(firstFixed, 0, {
action: 'set-ar-zoom',
label: 'Cover',
comment: 'Covers the entire screen, cropping as much as needed',
arguments: {
type: AspectRatioType.Cover
},
shortcut: {
key: 'w',
code: 'KeyW',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: true,
onKeyUp: true,
onKeyDown: false,
}
});
}
}
}
}
]);
export default ExtensionConfPatch;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
export enum RunLevel {
Off = 0,
UIOnly = 1,
CustomCSSActive = 2
}

View File

@ -1,274 +0,0 @@
import Debug from '../conf/Debug';
import PlayerData from './video-data/PlayerData';
import ExtensionMode from '../../common/enums/extension-mode.enum';
class ActionHandler {
constructor(pageInfo) {
this.logger = pageInfo.logger;
this.pageInfo = pageInfo;
this.settings = pageInfo.settings;
this.inputs = ['input', 'select', 'button', 'textarea'];
this.keyboardLocalDisabled = false;
}
init() {
this.logger.log('info', 'debug', "[ActionHandler::init] starting init");
this.keyUpActions = [];
this.keyDownActions = [];
this.mouseMoveActions = [];
this.mouseScrollUpActions = [];
this.mouseScrollDownActions = [];
this.mouseEnterActions = [];
this.mouseLeaveActions = [];
var ths = this;
var actions;
try {
if (this.settings.active.sites[window.location.host].actions) {
actions = this.settings.active.sites[window.location.host].actions;
} else {
actions = this.settings.active.actions;
}
} catch (e) {
actions = this.settings.active.actions;
}
for (var action of actions) {
if (!action.scopes) {
continue;
}
for (var scope in action.scopes) {
if (! action.scopes[scope].shortcut) {
continue;
}
var shortcut = action.scopes[scope].shortcut[0];
if (shortcut.onKeyDown) {
this.keyDownActions.push({
shortcut: shortcut,
cmd: action.cmd,
scope: scope,
});
}
if (shortcut.onKeyUp) {
this.keyUpActions.push({
shortcut: shortcut,
cmd: action.cmd,
scope: scope,
});
}
if (shortcut.onScrollUp) {
this.mouseScrollUpActions.push({
shortcut: shortcut,
cmd: action.cmd,
scope: scope,
});
}
if (shortcut.onScrollDown) {
this.mouseScrollDownActions.push({
shortcut: shortcut,
cmd: action.cmd,
scope: scope,
});
}
if (shortcut.onMouseEnter) {
this.mouseEnterActions.push({
shortcut: shortcut,
cmd: action.cmd,
scope: scope,
});
}
if (shortcut.onMouseLeave) {
this.mouseLeaveActions.push({
shortcut: shortcut,
cmd: action.cmd,
scope: scope,
});
}
if (shortcut.onMouseMove) {
this.mouseMoveActions.push({
shortcut: shortcut,
cmd: action.cmd,
scope: scope,
});
}
}
}
document.addEventListener('keydown', (event) => ths.handleKeydown(event) );
document.addEventListener('keyup', (event) => ths.handleKeyup(event) );
this.pageInfo.setActionHandler(this);
this.logger.log('info', 'debug', "[ActionHandler::init] initialization complete");
}
registerHandleMouse(videoData) {
this.logger.log('info', ['actionHandler', 'mousemove'], "[ActionHandler::registerHandleMouse] registering handle mouse for videodata:", videoData)
var ths = this;
if (videoData.player && videoData.player.element) {
videoData.player.element.addEventListener('mousemove', (event) => ths.handleMouseMove(event, videoData));
}
}
unregisterHandleMouse(videoData) {
var ths = this;
if (videoData.player && videoData.player.element) {
videoData.player.element.removeEventListener('mousemove', (event) => ths.handleMouseMove(event, videoData));
}
}
setKeyboardLocal(state) {
if (state === ExtensionMode.Enabled) {
this.keyboardLocalDisabled = false;
} else if (state === ExtensionMode.Disabled) {
this.keyboardLocalDisabled = true;
}
// don't do shit on invalid value of state
}
preventAction() {
var activeElement = document.activeElement;
if(this.logger.canLog('keyboard')) {
this.logger.pause(); // temp disable to avoid recursing;
this.logger.log('info', 'keyboard', "[ActionHandler::preventAction] Testing whether we're in a textbox or something. Detailed rundown of conditions:\n" +
"is full screen? (yes->allow):", PlayerData.isFullScreen(),
"\nis tag one of defined inputs? (yes->prevent):", this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1,
"\nis role = textbox? (yes -> prevent):", activeElement.getAttribute("role") === "textbox",
"\nis type === 'text'? (yes -> prevent):", activeElement.getAttribute("type") === "text",
"\nis keyboard local disabled? (yes -> prevent):", this.keyboardLocalDisabled,
"\nis keyboard enabled in settings? (no -> prevent)", this.settings.keyboardShortcutsEnabled(window.location.hostname),
"\nwill the action be prevented? (yes -> prevent)", this.preventAction(),
"\n-----------------{ extra debug info }-------------------",
"\ntag name? (lowercase):", activeElement.tagName, activeElement.tagName.toLocaleLowerCase(),
"\nrole:", activeElement.getAttribute('role'),
"\ntype:", activeElement.getAttribute('type'),
"insta-fail inputs:", this.inputs
);
this.logger.resume(); // undisable
}
// lately youtube has allowed you to read and write comments while watching video in
// fullscreen mode. We can no longer do this.
// if (PlayerData.isFullScreen()) {
// return false;
// }
if (this.keyboardLocalDisabled) {
return true;
}
if (!this.settings.keyboardShortcutsEnabled(window.location.hostname)) {
return true;
}
if (this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1) {
return true;
}
if (activeElement.getAttribute("role") === "textbox") {
return true;
}
if (activeElement.getAttribute("type") === "text") {
return true;
}
return false;
}
isActionMatch(shortcut, event) {
return shortcut.key === event.key &&
shortcut.ctrlKey === event.ctrlKey &&
shortcut.metaKey === event.metaKey &&
shortcut.altKey === event.altKey &&
shortcut.shiftKey === event.shiftKey
}
execAction(actions, event, videoData) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] Trying to find and execute action for event. Actions/event: ", "color: #ff0", actions, event);
for (var action of actions) {
if (this.isActionMatch(action.shortcut, event)) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] found an action associated with keypress/event: ", "color: #ff0", action);
for (var cmd of action.cmd) {
if (action.scope === 'page') {
if (cmd.action === "set-ar") {
this.pageInfo.setAr({type: cmd.arg, ratio: cmd.customArg});
} else if (cmd.action === "change-zoom") {
this.pageInfo.zoomStep(cmd.arg);
} else if (cmd.action === "set-zoom") {
this.pageInfo.setZoom(cmd.arg);
} else if (cmd.action === "set-stretch") {
this.pageInfo.setStretchMode(cmd.arg);
} else if (cmd.action === "toggle-pan") {
this.pageInfo.setPanMode(cmd.arg)
} else if (cmd.action === "pan") {
if (videoData) {
videoData.panHandler(event, true);
}
} else if (cmd.action === 'set-keyboard') {
this.setKeyboardLocal(cmd.arg);
}
} else {
let site = action.scope === 'site' ? window.location.host : '@global';
if (cmd.action === "set-stretch") {
this.settings.active.sites[site].stretch = cmd.arg;
} else if (cmd.action === "set-alignment") {
this.settings.active.sites[site].videoAlignment = cmd.arg;
} else if (cmd.action === "set-extension-mode") {
this.settings.active.sites[site].status = cmd.arg;
} else if (cmd.action === "set-autoar-mode") {
this.settings.active.sites[site].arStatus = cmd.arg;
} else if (cmd.action === 'set-keyboard') {
this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg;
}
this.settings.save();
}
}
// če smo našli dejanje za to tipko, potem ne preiskujemo naprej
// if we found an action for this key, we stop searching for a match
return;
}
}
}
handleKeyup(event) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::handleKeyup] we pressed a key: ", "color: #ff0", event.key , " | keyup: ", event.keyup, "event:", event);
if (this.preventAction()) {
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeyup] we are in a text box or something. Doing nothing.");
return;
}
this.execAction(this.keyUpActions, event);
}
handleKeydown(event) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::handleKeydown] we pressed a key: ", "color: #ff0", event.key , " | keydown: ", event.keydown, "event:", event)
if (this.preventAction()) {
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeydown] we are in a text box or something. Doing nothing.");
return;
}
this.execAction(this.keyDownActions, event);
}
handleMouseMove(event, videoData) {
this.logger.log('info', 'keyboard', "[ActionHandler::handleMouseMove] mouse move is being handled.\nevent:", event, "\nvideo data:", videoData);
videoData.panHandler(event);
this.execAction(this.mouseMoveActions, event, videoData)
}
}
export default ActionHandler;

View File

@ -1,12 +0,0 @@
class Interface {
constructor(videoData) {
this.conf = videoData;
this.player = videoData.player;
}
injectUi() {
this.detectorDiv = document.createElement('div');
this.uiRoot = document.createElement('div');
this.detectorDiv.appendChild(this.uiRoot);
}
}

View File

@ -1,211 +0,0 @@
import Debug from '../conf/Debug';
import currentBrowser from '../conf/BrowserDetect';
class Logger {
constructor(conf) {
this.initLogger();
if (conf) {
this.conf = conf;
}
this.history = [];
this.startTime = performance.now();
this.temp_disable = false;
}
initLogger() {
const ths = this;
const br = currentBrowser.firefox ? browser : chrome;
br.storage.onChanged.addListener( (changes, area) => {
if (Debug.debug && Debug.debugStorage) {
console.log("[Logger::<storage/on change>] Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area);
if (changes['uwLogger'] && changes['uwLogger'].newValue) {
console.log("[Logger::<storage/on change>] new settings object:", JSON.parse(changes.uwLogger.newValue));
}
}
if(changes['uwLogger'] && changes['uwLogger'].newValue) {
ths.conf = JSON.parse(changes.uwLogger.newValue);
}
});
}
async init() {
if (!this.conf) {
this.conf = await this.getSaved();
}
}
clear() {
this.log = [];
this.startTime = performance.now();
}
setConf(conf) {
this.conf = conf; // effective immediately
// also persist settings:
if (currentBrowser.firefox || currentBrowser.edge) {
return browser.storage.local.set( {'uwLogger': JSON.stringify(this.conf)});
} else if (currentBrowser.chrome) {
return chrome.storage.local.set( {'uwLogger': JSON.stringify(this.logger)});
}
}
async getSaved() {
let ret;
if (currentBrowser.firefox) {
ret = await browser.storage.local.get('uwLogger');
} else if (currentBrowser.chrome) {
ret = await new Promise( (resolve, reject) => {
chrome.storage.local.get('uwLogger', (res) => resolve(res));
});
} else if (currentBrowser.edge) {
ret = await new Promise( (resolve, reject) => {
browser.storage.local.get('uwLogger', (res) => resolve(res));
});
}
if (Debug.debug && Debug.debugStorage) {
try {
console.log("[Logger::getSaved] Got settings:", JSON.parse(ret.uwLogger));
} catch (e) {
console.log("[Logger::getSaved] No settings.")
}
}
try {
return JSON.parse(ret.uwLogger);
} catch(e) {
return {logToFile: false, logToConsole: false, consoleOptions: {}, fileOptions: {}};
}
}
// allow syncing of start times between bg and page scripts.
// may result in negative times in the log file, but that doesn't
// really matter
getStartTime() {
return this.startTime;
}
setStartTime(startTime) {
if (startTime) {
this.startTime = startTime;
} else {
this.startTime = performance.now();
}
}
getLogFileString() {
let logfileStr = '';
let logTs = ''; // number of seconds since extension started on a given page¸
for (let i = 0; i < this.history.length; i++) {
logTs = ((this.history[i].ts - Math.floor(this.performance.now)) / 3).toFixed(3);
logfileStr = `${logfileStr}[@${logTs}] -- ${this.history[i].message}\n`
}
return logfileStr;
}
pause() {
this.temp_disable = true;
}
resume() {
this.temp_disable = false;
}
canLog(component) {
return this.canLogFile(component) || this.canLogConsole(component);
}
canLogFile(component) {
if (!this.conf.fileOptions.enabled || this.temp_disable) {
return false;
}
if (component.length ) {
for (const c in component) {
if (this.conf.fileOptions[component]) {
return this.conf.fileOptions[component];
}
}
}
return this.conf.fileOptions[component];
}
canLogConsole(component) {
if (!this.conf.consoleOptions.enabled || this.temp_disable) {
return false;
}
if (Array.isArray(component) && component.length) {
for (const c in component) {
if (this.conf.consoleOptions[component]) {
return this.conf.consoleOptions[component];
}
}
}
return this.conf.consoleOptions[component];
}
// level is unused as of now, but this may change in the future
// levels: 'info', 'warn', 'error'
log(level, component, ...message) {
if (!this.conf) {
return;
}
if (this.conf.logToFile) {
if (this.canLogFile(component)) {
let ts = performance.now();
if (ts <= this.history[this.history.length - 1]) {
ts = this.history[this.history.length - 1] + 0.00001;
}
this.history.push({
ts: ts,
message: JSON.stringify(message),
})
}
}
if (this.conf.logToConsole) {
if (this.canLogConsole(component)) {
console.log(...message);
}
}
}
// leaves a noticeable mark in the file log at the time it got triggered, as well as
// at the intervals of 1s and .5s before the trigger moment
cahen() {
if (this.conf.logToFile) {
let ts = performance.now();
let secondMark = ts - 1000;
let halfSecondMark = ts - 500;
let i = this.history.length();
// correct ts _after_ secondMark and halfSecondMark were determined
if (ts <= this.history[this.history.length - 1]) {
ts = this.history[this.history.length - 1] + 0.00001;
}
this.history.push({
ts: ts,
message: "-------------------------------------- CAHEN --------------------------------------"
});
// find the spot for the half second mark. In this case, we don't really particularly care whether timestamps
// are duped due to cahen warnings
while (this.history[i--].ts > halfSecondMark) {}
this.history.push({
ts: halfSecondMark,
message: "-------------------------------------- 0.5s to CAHEN --------------------------------------"
});
// repeat for one second warning
while (this.history[i--].ts > secondMark) {}
this.history.push({
ts: secondMark,
message: "-------------------------------------- 1s to CAHEN --------------------------------------"
});
}
}
}
export default Logger;

View File

@ -1,189 +0,0 @@
class PlayerPickerHelper {
constructor (settings, callbacks) {
this.settings = settings;
this.videos = document.selectElementsByTagName('video');
this.selectedParentIndex = this.findPlayerForVideos(settings, this.videos)[0];
this.savedCss = [];
this.markVideos();
this.markIndexForAll(index);
this.markInitialQuerySelectors();
}
/*
*
* Internal functions
*
*/
saveBorder(element) {
if (this.savedCss.findIndex(x => x.element === element) !== -1) {
this.savedCss.push({element: element, border: element.style.border});
}
}
restoreBorders() {
for (const e of this.savedCss) {
e.element.style.border = e.border;
}
}
findPlayerForVideos(settings, videos) {
const playerIndexes = [];
for (const v of videos) {
playerIndexes.push(this.findPlayerForVideo(settings, v));
}
return playerIndexes;
}
findPlayerForVideo(settings, video) {
const host = window.location.host;
let element = video.parentNode;
if (this.settings.active.sites[host]
&& this.settings.active.sites[host].DOM
&& this.settings.active.sites[host].DOM.player
&& this.settings.active.sites[host].DOM.player.manual) {
if (this.settings.active.sites[host].DOM.player.useRelativeAncestor
&& this.settings.active.sites[host].DOM.player.videoAncestor) {
return this.settings.active.sites[host].DOM.player.videoAncestor;
} else if (this.settings.active.sites[host].DOM.player.querySelectors) {
const allSelectors = document.querySelectorAll(this.settings.active.sites[host].DOM.player.querySelectors);
let elementIndex = 1;
while (element && !this.collectionHas(allSelectors, element)) {
element = element.parentNode;
elementIndex++;
}
return elementIndex;
}
}
let elementIndex = 0;
var trustCandidateAfterGrows = 2; // if candidate_width or candidate_height increases in either dimensions this many
// times, we say we found our player. (This number ignores weird elements)
// in case our <video> is bigger than player in one dimension but smaller in the other
// if site is coded properly, player can't be wider than that
var candidate_width = Math.max(element.offsetWidth, window.innerWidth);
var candidate_height = Math.max(element.offsetHeight, window.innerHeight);
var playerCandidateNode = element;
// if we haven't found element using fancy methods, we resort to the good old fashioned way
var grows = trustCandidateAfterGrows;
while(element != undefined){
// odstranimo čudne elemente, ti bi pokvarili zadeve
// remove weird elements, those would break our stuff
if ( element.offsetWidth == 0 || element.offsetHeight == 0){
element = element.parentNode;
elementIndex++;
continue;
}
if ( element.offsetHeight <= candidate_height &&
element.offsetWidth <= candidate_width ){
// if we're in fullscreen, we only consider elements that are exactly as big as the monitor.
if( ! isFullScreen ||
(element.offsetWidth == window.innerWidth && element.offsetHeight == window.innerHeight) ){
playerCandidateNode = element;
candidate_width = element.offsetWidth;
candidate_height = element.offsetHeight;
grows = trustCandidateAfterGrows;
this.logger.log('info', 'debug', "Found new candidate for player. Dimensions: w:", candidate_width, "h:",candidate_height, "node:", playerCandidateNode);
}
}
else if(grows --<= 0){
this.logger.log('info', 'playerDetect', "Current element grew in comparrison to the child. We probably found the player. breaking loop, returning current result");
break;
}
element = element.parentNode;
elementIndex++;
}
return elementIndex;
}
markVideos() {
for (const v of this.videos) {
this.markVideo(v);
}
}
markVideo(video) {
this.saveBorder(video);
video.style.border = "1px solid #00f";
}
markIndexForAll(index){
for (const v of this.videos) {
this.markIndex(index, v);
}
}
markIndex(index, video) {
el = video.parentNode;
while (index --> 1) {
el = el.parentNode;
}
this.saveBorder(el);
el.style.border = "1px solid #88f";
}
markInitialQuerySelectors() {
try {
if (this.settings.active.sites[host].DOM.player.querySelectors.trim()) {
this.markQuerySelectorMatches(this.settings.active.sites[host].DOM.player.querySelectors);
}
} catch (e) {
// nothing to see here. something in that if spaghett is undefined, which causes
// everything to fail. Since this means we've got zero query string matches to mark,
// we just ignore the failure
}
}
markQuerySelectorMatches(qsString) {
const allSelectors = document.querySelectorAll(qsString);
for (e of allSelectors) {
this.saveBorder(e);
e.style.border = "1px dashed fd2";
}
}
markQsPlayerDetection(qsString, index, video) {
let element = video.parentNode;
let elementIndex = 1;
const allSelectors = document.querySelectorAll(qsString);
while (element && !this.collectionHas(allSelectors, element)) {
element = element.parentNode;
elementIndex++;
}
this.saveBorder(element)
if (elementIndex > index) {
element.style.border = "2px solid #f00"
} else if (elementIndex === index) {
element.style.border = "2px solid #027a5c"
}
}
/*
*
*
* Function that actually interface with playerpicker and do stuff
*
*
*/
setQuerySelectors(querySelectorString) {
}
}
export default PlayerPickerHelper

View File

@ -1,535 +0,0 @@
import Debug from '../conf/Debug';
import currentBrowser from '../conf/BrowserDetect';
import ExtensionConf from '../conf/ExtensionConf';
import ExtensionMode from '../../common/enums/extension-mode.enum';
import ObjectCopy from '../lib/ObjectCopy';
import Stretch from '../../common/enums/stretch.enum';
import VideoAlignment from '../../common/enums/video-alignment.enum';
import ExtensionConfPatch from '../conf/ExtConfPatches';
class Settings {
constructor(options) {
// Options: activeSettings, updateCallback, logger
this.logger = options.logger;
const activeSettings = options.activeSettings;
const updateCallback = options.updateCallback;
this.active = activeSettings ? activeSettings : undefined;
this.default = ExtensionConf;
this.default['version'] = this.getExtensionVersion();
this.useSync = false;
this.version = undefined;
this.updateCallback = updateCallback;
const ths = this;
if(currentBrowser.firefox) {
browser.storage.onChanged.addListener( (changes, area) => {
this.logger.log('info', 'settings', "[Settings::<storage/on change>] Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area);
if (changes['uwSettings'] && changes['uwSettings'].newValue) {
this.logger.log('info', 'settings',"[Settings::<storage/on change>] new settings object:", JSON.parse(changes.uwSettings.newValue));
}
if(changes['uwSettings'] && changes['uwSettings'].newValue) {
ths.setActive(JSON.parse(changes.uwSettings.newValue));
}
if(this.updateCallback) {
try {
updateCallback(ths);
} catch (e) {
this.logger.log('error', 'settings', "[Settings] CALLING UPDATE CALLBACK FAILED.")
}
}
});
} else if (currentBrowser.chrome) {
chrome.storage.onChanged.addListener( (changes, area) => {
this.logger.log('info', 'settings', "[Settings::<storage/on change>] Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area);
if (changes['uwSettings'] && changes['uwSettings'].newValue) {
this.logger.log('info', 'settings',"[Settings::<storage/on change>] new settings object:", JSON.parse(changes.uwSettings.newValue));
}
if(changes['uwSettings'] && changes['uwSettings'].newValue) {
ths.setActive(JSON.parse(changes.uwSettings.newValue));
}
if(this.updateCallback) {
try {
updateCallback(ths);
} catch (e) {
this.logger.log('error', 'settings',"[Settings] CALLING UPDATE CALLBACK FAILED.")
}
}
});
}
}
getExtensionVersion() {
if (currentBrowser.firefox) {
return browser.runtime.getManifest().version;
} else if (currentBrowser.chrome) {
return chrome.runtime.getManifest().version;
} else if (currentBrowser.edge) {
return browser.runtime.getManifest().version;
}
}
compareExtensionVersions(a, b) {
let aa = a.split('.');
let bb = b.split('.');
if (+aa[0] !== +bb[0]) {
// difference on first digit
return +aa[0] - +bb[0];
} if (+aa[1] !== +bb[1]) {
// first digit same, difference on second digit
return +aa[1] - +bb[1];
} if (+aa[2] !== +bb[2]) {
return +aa[2] - +bb[2];
// first two digits the same, let's check the third digit
} else {
// fourth digit is optional. When not specified, 0 is implied
// btw, ++(aa[3] || 0) - ++(bb[3] || 0) doesn't work
// Since some things are easier if we actually have a value for
// the fourth digit, we turn a possible undefined into a zero
aa[3] = aa[3] === undefined ? 0 : aa[3];
bb[3] = bb[3] === undefined ? 0 : bb[3];
// also, the fourth digit can start with a letter.
// versions that start with a letter are ranked lower than
// versions x.x.x.0
if (isNaN(+aa[3]) ^ isNaN(+bb[3])) {
return isNaN(+aa[3]) ? -1 : 1;
}
// at this point, either both version numbers are a NaN or
// both versions are a number.
if (!isNaN(+aa[3])) {
return +aa[3] - +bb[3];
}
// letters have their own hierarchy:
// dev < a < b < rc
let av = this.getPrereleaseVersionHierarchy(aa[3]);
let bv = this.getPrereleaseVersionHierarchy(bb[3]);
if (av !== bv) {
return av - bv;
} else {
return +(aa[3].replace(/\D/g,'')) - +(bb[3].replace(/\D/g, ''));
}
}
}
getPrereleaseVersionHierarchy(version) {
if (version.startsWith('dev')) {
return 0;
}
if (version.startsWith('a')) {
return 1;
}
if (version.startsWith('b')) {
return 2;
}
return 3;
}
sortConfPatches(patchesIn) {
return patchesIn.sort( (a, b) => this.compareExtensionVersions(a.forVersion, b.forVersion));
}
findFirstNecessaryPatch(version, extconfPatches) {
const sorted = this.sortConfPatches(extconfPatches);
return sorted.findIndex(x => this.compareExtensionVersions(x.forVersion, version) > 0);
}
applySettingsPatches(oldVersion, patches) {
let index = this.findFirstNecessaryPatch(oldVersion, patches);
if (index === -1) {
this.logger.log('info','settings','[Settings::applySettingsPatches] There are no pending conf patches.');
return;
}
// apply all remaining patches
this.logger.log('info', 'settings', `[Settings::applySettingsPatches] There are ${patches.length - index} settings patches to apply`);
while (index < patches.length) {
delete patches[index].forVersion;
ObjectCopy.overwrite(this.active, patches[index]);
index++;
}
}
async init() {
const settings = await this.get();
this.version = this.getExtensionVersion();
// |—> on first setup, settings is undefined & settings.version is haram
// | since new installs ship with updates by default, no patching is
// | needed. In this case, we assume we're on the current version
const oldVersion = (settings && settings.version) || this.version;
if(Debug.debug) {
this.logger.log('info', 'settings', "[Settings::init] Configuration fetched from storage:", settings,
"\nlast saved with:", settings.version,
"\ncurrent version:", this.version
);
// if (Debug.flushStoredSettings) {
// this.logger.log('info', 'settings', "%c[Settings::init] Debug.flushStoredSettings is true. Using default settings", "background: #d00; color: #ffd");
// Debug.flushStoredSettings = false; // don't do it again this session
// this.active = this.getDefaultSettings();
// this.active.version = this.version;
// this.set(this.active);
// return this.active;
// }
}
// if there's no settings saved, return default settings.
if(! settings || (Object.keys(settings).length === 0 && settings.constructor === Object)) {
this.logger.log(
'info',
'settings',
'[Settings::init] settings don\'t exist. Using defaults.\n#keys:',
settings ? Object.keys(settings).length : 0,
'\nsettings:',
settings
);
this.active = this.getDefaultSettings();
this.active.version = this.version;
await this.save();
return this.active;
}
// if there's settings, set saved object as active settings
this.active = settings;
// if last saved settings was for version prior to 4.x, we reset settings to default
// it's not like people will notice cos that version didn't preserve settings at all
if (this.active.version && !settings.version.startsWith('4')) {
this.active = this.getDefaultSettings();
this.active.version = this.version;
await this.save();
return this.active;
}
// if version number is undefined, we make it defined
// this should only happen on first extension initialization
if (!this.active.version) {
this.active.version = this.version;
await this.save();
return this.active;
}
// check if extension has been updated. If not, return settings as they were retrieved
if (this.active.version === this.version) {
this.logger.log('info', 'settings', "[Settings::init] extension was saved with current version of ultrawidify. Returning object as-is.");
return this.active;
}
// This means extension update happened.
// btw fun fact — we can do version rollbacks, which might come in handy while testing
this.active.version = this.version;
// if extension has been updated, update existing settings with any options added in the
// new version. In addition to that, we remove old keys that are no longer used.
const patched = ObjectCopy.addNew(settings, this.default);
this.logger.log('info', 'settings',"[Settings.init] Results from ObjectCopy.addNew()?", patched, "\n\nSettings from storage", settings, "\ndefault?", this.default);
if (patched) {
this.active = patched;
}
// in case settings in previous version contained a fucky wucky, we overwrite existing settings with a patch
this.applySettingsPatches(oldVersion, ExtensionConfPatch);
// set 'whatsNewChecked' flag to false when updating, always
this.active.whatsNewChecked = false;
// update settings version to current
this.active.version = this.version;
await this.save();
return this.active;
}
async get() {
let ret;
if (currentBrowser.firefox) {
ret = await browser.storage.local.get('uwSettings');
} else if (currentBrowser.chrome) {
ret = await new Promise( (resolve, reject) => {
chrome.storage.local.get('uwSettings', (res) => resolve(res));
});
} else if (currentBrowser.edge) {
ret = await new Promise( (resolve, reject) => {
browser.storage.local.get('uwSettings', (res) => resolve(res));
});
}
this.logger.log('info', 'settings', 'Got settings:', ret && ret.uwSettings && JSON.parse(ret.uwSettings));
try {
return JSON.parse(ret.uwSettings);
} catch(e) {
return undefined;
}
}
async set(extensionConf) {
extensionConf.version = this.version;
this.logger.log('info', 'settings', "[Settings::set] setting new settings:", extensionConf)
if (currentBrowser.firefox || currentBrowser.edge) {
return browser.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
} else if (currentBrowser.chrome) {
return chrome.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
}
}
async setActive(activeSettings) {
this.active = activeSettings;
}
async setProp(prop, value) {
this.active[prop] = value;
}
async save() {
if (Debug.debug && Debug.storage) {
console.log("[Settings::save] Saving active settings:", this.active);
}
await this.set(this.active);
}
async rollback() {
this.active = await this.get();
}
getDefaultSettings() {
return JSON.parse(JSON.stringify(this.default));
}
// -----------------------------------------
// Nastavitve za posamezno stran
// Config for a given page:
//
// <hostname> : {
// status: <option> // should extension work on this site?
// arStatus: <option> // should we do autodetection on this site?
// statusEmbedded: <option> // reserved for future... maybe
// }
//
// Veljavne vrednosti za možnosti
// Valid values for options:
//
// status, arStatus, statusEmbedded:
//
// * enabled — always allow
// * basic — only allow fullscreen
// * default — allow if default is to allow, block if default is to block
// * disabled — never allow
getActionsForSite(site) {
if (!site) {
return this.active.actions;
}
if (this.active.sites[site] && this.active.sites[site].actions && this.active.sites[site].actions.length > 0) {
return this.active.sites[site].actions;
}
return this.active.actions;
}
getExtensionMode(site) {
if (!site) {
site = window.location.hostname;
if (!site) {
this.logger.log('info', 'settings', `[Settings::canStartExtension] window.location.hostname is null or undefined: ${window.location.hostname} \nactive settings:`, this.active);
return ExtensionMode.Disabled;
}
}
try {
// if site-specific settings don't exist for the site, we use default mode:
if (! this.active.sites[site]) {
return this.getExtensionMode('@global');
}
if (this.active.sites[site].mode === ExtensionMode.Enabled) {
return ExtensionMode.Enabled;
} else if (this.active.sites[site].mode === ExtensionMode.Basic) {
return ExtensionMode.Basic;
} else if (this.active.sites[site].mode === ExtensionMode.Default && site !== '@global') {
return this.getExtensionMode('@global');
} else {
return ExtensionMode.Disabled;
}
} catch(e){
this.logger.log('error', 'settings', "[Settings.js::canStartExtension] Something went wrong — are settings defined/has init() been called?\n\nerror:", e, "\n\nSettings object:", this)
return ExtensionMode.Disabled;
}
}
canStartExtension(site) {
// returns 'true' if extension can be started on a given site. Returns false if we shouldn't run.
if (!site) {
site = window.location.hostname;
if (!site) {
this.logger.log('info', 'settings', `[Settings::canStartExtension] window.location.hostname is null or undefined: ${window.location.hostname} \nactive settings:`, this.active);
return false;
}
}
// if (Debug.debug) {
// // let's just temporarily disable debugging while recursively calling
// // this function to get extension status on current site without duplo
// // console logs (and without endless recursion)
// Debug.debug = false;
// const cse = this.canStartExtension(site);
// Debug.debug = true;
// }
try{
// if site is not defined, we use default mode:
if (! this.active.sites[site] || this.active.sites[site].mode === ExtensionMode.Default) {
return this.active.sites['@global'].mode === ExtensionMode.Enabled;
}
if (this.active.sites['@global'].mode === ExtensionMode.Enabled) {
return this.active.sites[site].mode !== ExtensionMode.Disabled;
} else if (this.active.sites['@global'].mode === ExtensionMode.Whitelist) {
return this.active.sites[site].mode === ExtensionMode.Enabled;
} else {
return false;
}
} catch(e) {
this.logger.log('error', 'settings', "[Settings.js::canStartExtension] Something went wrong — are settings defined/has init() been called?\nSettings object:", this);
return false;
}
}
keyboardShortcutsEnabled(site) {
if (!site) {
site = window.location.hostname;
}
if (!site) {
return false;
}
try {
if (!this.active.sites[site]
|| this.active.sites[site].keyboardShortcutsEnabled === undefined
|| this.active.sites[site].keyboardShortcutsEnabled === ExtensionMode.Default) {
return this.keyboardShortcutsEnabled('@global');
} else {
return this.active.sites[site].keyboardShortcutsEnabled === ExtensionMode.Enabled;
}
} catch (e) {
this.logger.log('info', 'settings',"[Settings.js::keyboardDisabled] something went wrong:", e);
return false;
}
}
extensionEnabled(){
return this.active.sites['@global'] !== ExtensionMode.Disabled
}
extensionEnabledForSite(site) {
return this.canStartExtension(site);
}
canStartAutoAr(site) {
// 'site' argument is only ever used when calling this function recursively for debugging
if (!site) {
site = window.location.host;
if (!site) {
return false;
}
}
if (Debug.debug) {
// let's just temporarily disable debugging while recursively calling
// this function to get extension status on current site without duplo
// console logs (and without endless recursion)
Debug.debug = false;
const csar = this.canStartAutoAr(site);
Debug.debug = true;
this.logger.log('info', 'settings', "[Settings::canStartAutoAr] ----------------\nCAN WE START AUTOAR ON SITE", site,
"?\n\nsettings.active.sites[site]=", this.active.sites[site], "settings.active.sites[@global]=", this.active.sites['@global'],
"\nAutoar mode (global)?", this.active.sites['@global'].autoar,
`\nAutoar mode (${site})`, this.active.sites[site] ? this.active.sites[site].autoar : '<not defined>',
"\nCan autoar be started?", csar
);
}
// if site is not defined, we use default mode:
if (! this.active.sites[site]) {
return this.active.sites['@global'].autoar === ExtensionMode.Enabled;
}
if (this.active.sites['@global'].autoar === ExtensionMode.Enabled) {
return this.active.sites[site].autoar !== ExtensionMode.Disabled;
} else if (this.active.sites['@global'].autoar === ExtensionMode.Whitelist) {
this.logger.log('info', 'settings', "canStartAutoAr — can(not) start csar because extension is in whitelist mode, and this site is (not) equal to", ExtensionMode.Enabled)
return this.active.sites[site].autoar === ExtensionMode.Enabled;
} else {
this.logger.log('info', 'settings', "canStartAutoAr — cannot start csar because extension is globally disabled")
return false;
}
}
getDefaultOption(option) {
const allDefault = {
mode: ExtensionMode.Default,
autoar: ExtensionMode.Default,
autoarFallback: ExtensionMode.Default,
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
};
if (!option || allDefault[option] === undefined) {
return allDefault;
}
return allDefault[option];
}
getDefaultAr(site) {
// site = this.getSiteSettings(site);
// if (site.defaultAr) {
// return site.defaultAr;
// }
return this.active.miscSettings.defaultAr;
}
getDefaultStretchMode(site) {
if (site && this.active.sites[site] && this.active.sites[site].stretch !== Stretch.Default) {
return this.active.sites[site].stretch;
}
return this.active.sites['@global'].stretch;
}
getDefaultVideoAlignment(site) {
if (site && this.active.sites[site] && this.active.sites[site].videoAlignment !== VideoAlignment.Default) {
return this.active.sites[site].videoAlignment;
}
return this.active.sites['@global'].videoAlignment;
}
}
export default Settings;

View File

@ -1,897 +0,0 @@
import Debug from '../../conf/Debug';
import EdgeDetect from './edge-detect/EdgeDetect';
import EdgeStatus from './edge-detect/enums/EdgeStatusEnum';
import EdgeDetectPrimaryDirection from './edge-detect/enums/EdgeDetectPrimaryDirectionEnum';
import EdgeDetectQuality from './edge-detect/enums/EdgeDetectQualityEnum';
import GuardLine from './GuardLine';
import DebugCanvas from './DebugCanvas';
import VideoAlignment from '../../../common/enums/video-alignment.enum';
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
class ArDetector {
constructor(videoData){
this.logger = videoData.logger;
this.conf = videoData;
this.video = videoData.video;
this.settings = videoData.settings;
this.setupTimer = null;
this.sampleCols = [];
this.canFallback = true;
this.fallbackMode = false;
this.blackLevel = this.settings.active.arDetect.blackbar.blackLevel;
this.arid = (Math.random()*100).toFixed();
// ar detector starts in this state. running main() sets both to false
this._halted = true;
this._exited = true;
// we can tick manually, for debugging
this._manualTicks = false;
this._nextTick = false;
this.canDoFallbackMode = false;
this.logger.log('info', 'init', `[ArDetector::ctor] creating new ArDetector. arid: ${this.arid}`);
}
setManualTick(manualTick) {
this._manualTicks = manualTick;
}
tick() {
this._nextTick = true;
}
init(){
this.logger.log('info', 'init', `[ArDetect::init] <@${this.arid}> Initializing autodetection.`);
try {
if (this.settings.canStartAutoAr()) {
this.setup();
} else {
throw "Settings prevent autoar from starting"
}
} catch (e) {
this.logger.log('error', 'init', `%c[ArDetect::init] <@${this.arid}> Initialization failed.`, _ard_console_stop, e);
}
}
destroy(){
this.logger.log('info', 'init', `%c[ArDetect::destroy] <@${this.arid}> Destroying aard.`, _ard_console_stop, e);
// this.debugCanvas.destroy();
this.stop();
}
setup(cwidth, cheight){
this.logger.log('info', 'init', `[ArDetect::setup] <@${this.arid}> Starting autodetection setup.`);
//
// [-1] check for zero-width and zero-height videos. If we detect this, we kick the proverbial
// can some distance down the road. This problem will prolly fix itself soon. We'll also
// not do any other setup until this issue is fixed
//
if(this.video.videoWidth === 0 || this.video.videoHeight === 0 ){
this.logger.log('warn', 'debug', `[ArDetect::setup] <@${this.arid}> This video has zero width or zero height. Dimensions: ${this.video.videoWidth} × ${this.video.videoHeight}`);
this.scheduleInitRestart();
return;
}
//
// [0] initiate "dependencies" first
//
this.guardLine = new GuardLine(this);
this.edgeDetector = new EdgeDetect(this);
// this.debugCanvas = new DebugCanvas(this);
//
// [1] initiate canvases
//
if (!cwidth) {
cwidth = this.settings.active.arDetect.canvasDimensions.sampleCanvas.width;
cheight = this.settings.active.arDetect.canvasDimensions.sampleCanvas.height;
}
if (this.canvas) {
this.canvas.remove();
}
if (this.blackframeCanvas) {
this.blackframeCanvas.remove();
}
// things to note: we'll be keeping canvas in memory only.
this.canvas = document.createElement("canvas");
this.canvas.width = cwidth;
this.canvas.height = cheight;
this.blackframeCanvas = document.createElement("canvas");
this.blackframeCanvas.width = this.settings.active.arDetect.canvasDimensions.blackframeCanvas.width;
this.blackframeCanvas.height = this.settings.active.arDetect.canvasDimensions.blackframeCanvas.height;
this.context = this.canvas.getContext("2d");
this.blackframeContext = this.blackframeCanvas.getContext("2d");
// do setup once
// tho we could do it for every frame
this.canvasScaleFactor = cheight / this.video.videoHeight;
//
// [2] determine places we'll use to sample our main frame
//
var ncol = this.settings.active.arDetect.sampling.staticCols;
var nrow = this.settings.active.arDetect.sampling.staticRows;
var colSpacing = this.canvas.width / ncol;
var rowSpacing = (this.canvas.height << 2) / nrow;
this.sampleLines = [];
this.sampleCols = [];
for(var i = 0; i < ncol; i++){
if(i < ncol - 1)
this.sampleCols.push(Math.round(colSpacing * i));
else{
this.sampleCols.push(Math.round(colSpacing * i) - 1);
}
}
for(var i = 0; i < nrow; i++){
if(i < ncol - 5)
this.sampleLines.push(Math.round(rowSpacing * i));
else{
this.sampleLines.push(Math.round(rowSpacing * i) - 4);
}
}
//
// [3] detect if we're in the fallback mode and reset guardline
//
if (this.fallbackMode) {
this.logger.log('warn', 'debug', `[ArDetect::setup] <@${this.arid}> WARNING: CANVAS RESET DETECTED/we're in fallback mode - recalculating guardLine`, "background: #000; color: #ff2");
// blackbar, imagebar
this.guardLine.reset();
}
//
// [4] see if browser supports "fallback mode" by drawing a small portion of our window
//
try {
this.blackframeContext.drawWindow(window,0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height, "rgba(0,0,128,1)");
this.canDoFallbackMode = true;
} catch (e) {
this.canDoFallbackMode = false;
}
//
// [5] do other things setup needs to do
//
this.detectionTimeoutEventCount = 0;
this.resetBlackLevel();
// if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio
this.conf.resizer.setLastAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
this.canvasImageDataRowLength = cwidth << 2;
this.noLetterboxCanvasReset = false;
if (this.settings.canStartAutoAr() ) {
this.start();
}
if(Debug.debugCanvas.enabled){
// this.debugCanvas.init({width: cwidth, height: cheight});
// DebugCanvas.draw("test marker","test","rect", {x:5, y:5}, {width: 5, height: 5});
}
this.conf.arSetupComplete = true;
}
start() {
this.logger.log('info', 'debug', `"%c[ArDetect::start] <@${this.arid}> Starting automatic aspect ratio detection`, _ard_console_start);
if (this.conf.resizer.lastAr.type === AspectRatio.Automatic) {
// ensure first autodetection will run in any case
this.conf.resizer.setLastAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
}
// launch main() if it's currently not running:
this.main();
// automatic detection starts halted. If halted=false when main first starts, extension won't run
// this._paused is undefined the first time we run this function, which is effectively the same thing
// as false. Still, we'll explicitly fix this here.
this._paused = false;
this._halted = false;
this._paused = false;
}
unpause() {
// pause only if we were running before. Don't pause if we aren't running
// (we are running when _halted is neither true nor undefined)
if (this._paused && this._halted === false) {
this._paused = true;
}
}
pause() {
// pause only if we were running before. Don't pause if we aren't running
// (we are running when _halted is neither true nor undefined)
if (this._halted === false) {
this._paused = true;
}
}
stop(){
this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> Stopping automatic aspect ratio detection`, _ard_console_stop);
this._halted = true;
// this.conf.resizer.setArLastAr();
}
async main() {
if (this._paused) {
// unpause if paused
this._paused = false;
return; // main loop still keeps executing. Return is needed to avoid a million instances of autodetection
}
if (!this._halted) {
// we are already running, don't run twice
// this would have handled the 'paused' from before, actually.
return;
}
let exitedRetries = 10;
while (!this._exited && exitedRetries --> 0) {
this.logger.log('warn', 'debug', `[ArDetect::main] <@${this.arid}> We are trying to start another instance of autodetection on current video, but the previous instance hasn't exited yet. Waiting for old instance to exit ...`);
await this.sleep(this.settings.active.arDetect.timers.tickrate);
}
if (!this._exited) {
this.logger.log('error', 'debug', `[ArDetect::main] <@${this.arid}> Previous instance didn't exit in time. Not starting a new one.`);
return;
}
this.logger.log('info', 'debug', `%c[ArDetect::main] <@${this.arid}> Previous instance didn't exit in time. Not starting a new one.`);
// we need to unhalt:
this._halted = false;
this._exited = false;
// set initial timestamps so frame check will trigger the first time we run the loop
let lastFrameCheckStartTime = Date.now() - (this.settings.active.arDetect.timers.playing << 1);
const frameCheckTimes = new Array(10).fill(-1);
let frameCheckBufferIndex = 0;
let fcstart, fctime;
while (this && !this._halted) {
// NOTE: we separated tickrate and inter-check timeouts so that when video switches
// state from 'paused' to 'playing', we don't need to wait for the rest of the longer
// paused state timeout to finish.
if ( (!this._manualTicks && this.canTriggerFrameCheck(lastFrameCheckStartTime)) || this._nextTick) {
this._nextTick = false;
lastFrameCheckStartTime = Date.now();
fcstart = performance.now();
try {
this.frameCheck();
} catch (e) {
this.logger.log('error', 'debug', `%c[ArDetect::main] <@${this.arid}> Frame check failed:`, "color: #000, background: #f00", e);
}
if (Debug.performanceMetrics) {
fctime = performance.now() - fcstart;
frameCheckTimes[frameCheckBufferIndex % frameCheckTimes.length] = fctime;
this.conf.pageInfo.sendPerformanceUpdate({frameCheckTimes: frameCheckTimes, lastFrameCheckTime: fctime});
++frameCheckBufferIndex;
}
}
await this.sleep(this.settings.active.arDetect.timers.tickrate);
}
this.logger.log('info', 'debug', `%c[ArDetect::main] <@${this.arid}> Main autodetection loop exited. Halted? ${this._halted}`, _ard_console_stop);
this._exited = true;
}
async sleep(timeout) {
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
}
canTriggerFrameCheck(lastFrameCheckStartTime) {
if (this._paused) {
return false;
}
if (this.video.ended || this.video.paused){
// we slow down if ended or pausing. Detecting is pointless.
// we don't stop outright in case seeking happens during pause/after video was
// ended and video gets into 'playing' state again
return Date.now() - lastFrameCheckStartTime > this.settings.active.arDetect.timers.paused;
}
if (this.video.error){
// če je video pavziran, še vedno skušamo zaznati razmerje stranic - ampak bolj poredko.
// if the video is paused, we still do autodetection. We just do it less often.
return Date.now() - lastFrameCheckStartTime > this.settings.active.arDetect.timers.error;
}
return Date.now() - lastFrameCheckStartTime > this.settings.active.arDetect.timers.playing;
}
isRunning(){
return ! (this._halted || this._paused || this._exited);
}
scheduleInitRestart(timeout, force_reset){
if(! timeout){
timeout = 100;
}
// don't allow more than 1 instance
if(this.setupTimer){
clearTimeout(this.setupTimer);
}
var ths = this;
this.setupTimer = setTimeout(function(){
ths.setupTimer = null;
try{
ths.main();
} catch(e) {
this.logger('error', 'debug', `[ArDetector::scheduleInitRestart] <@${this.arid}> Failed to start main(). Error:`,e);
}
ths = null;
},
timeout
);
}
//#region helper functions (general)
attachCanvas(canvas){
if(this.attachedCanvas)
this.attachedCanvas.remove();
// todo: place canvas on top of the video instead of random location
canvas.style.position = "absolute";
canvas.style.left = "200px";
canvas.style.top = "1200px";
canvas.style.zIndex = 10000;
document.getElementsByTagName("body")[0]
.appendChild(canvas);
}
canvasReadyForDrawWindow(){
this.logger.log('info', 'debug', `%c[ArDetect::canvasReadyForDrawWindow] <@${this.arid}> canvas is ${this.canvas.height === window.innerHeight ? '' : 'NOT '}ready for drawWindow(). Canvas height: ${this.canvas.height}px; window inner height: ${window.innerHeight}px.`)
return this.canvas.height == window.innerHeight
}
getTimeout(baseTimeout, startTime){
var execTime = (performance.now() - startTime);
return baseTimeout;
}
//#endregion
getDefaultAr() {
return this.video.videoWidth / this.video.videoHeight;
}
calculateArFromEdges(edges) {
// if we don't specify these things, they'll have some default values.
if(edges.top === undefined){
edges.top = 0;
edges.bottom = 0;
edges.left = 0; // RESERVED FOR FUTURE — CURRENTLY UNUSED
edges.right = 0; // THIS FUNCTION CAN PRESENTLY ONLY HANDLE LETTERBOX
}
let letterbox = edges.top + edges.bottom;
if (! this.fallbackMode) {
// Since video is stretched to fit the canvas, we need to take that into account when calculating target
// aspect ratio and correct our calculations to account for that
const fileAr = this.video.videoWidth / this.video.videoHeight;
const canvasAr = this.canvas.width / this.canvas.height;
let widthCorrected;
if (edges.top && edges.bottom) {
// in case of letterbox, we take canvas height as canon and assume width got stretched or squished
if (fileAr != canvasAr) {
widthCorrected = this.canvas.height * fileAr;
} else {
widthCorrected = this.canvas.width;
}
return widthCorrected / (this.canvas.height - letterbox);
}
} else {
// fallback mode behaves a wee bit differently
let zoomFactor = 1;
// there's stuff missing from the canvas. We need to assume canvas' actual height is bigger by a factor x, where
// x = [video.zoomedHeight] / [video.unzoomedHeight]
//
// letterbox also needs to be corrected:
// letterbox += [video.zoomedHeight] - [video.unzoomedHeight]
var vbr = this.video.getBoundingClientRect();
zoomFactor = vbr.height / this.video.clientHeight;
letterbox += vbr.height - this.video.clientHeight;
var trueHeight = this.canvas.height * zoomFactor - letterbox;
if(edges.top > 1 && edges.top <= this.settings.active.arDetect.fallbackMode.noTriggerZonePx ){
this.logger.log('info', 'arDetect', `%c[ArDetect::calculateArFromEdges] <@${this.arid}> Edge is in the no-trigger zone. Aspect ratio change is not triggered.`)
return;
}
// varnostno območje, ki naj ostane črno (da lahko v fallback načinu odkrijemo ožanje razmerja stranic).
// x2, ker je safetyBorderPx definiran za eno stran.
// safety border so we can detect aspect ratio narrowing (21:9 -> 16:9).
// x2 because safetyBorderPx is for one side.
trueHeight += (this.settings.active.arDetect.fallbackMode.safetyBorderPx << 1);
return this.canvas.width * zoomFactor / trueHeight;
}
}
processAr(trueAr){
this.detectedAr = trueAr;
// poglejmo, če se je razmerje stranic spremenilo
// check if aspect ratio is changed:
var lastAr = this.conf.resizer.getLastAr();
if (lastAr.type === AspectRatio.Automatic && lastAr.ratio !== null){
// spremembo lahko zavrnemo samo, če uporabljamo avtomatski način delovanja in če smo razmerje stranic
// že nastavili.
//
// we can only deny aspect ratio changes if we use automatic mode and if aspect ratio was set from here.
var arDiff = trueAr - lastAr.ar;
if (arDiff < 0)
arDiff = -arDiff;
var arDiff_percent = arDiff / trueAr;
// ali je sprememba v mejah dovoljenega? Če da -> fertik
// is ar variance within acceptable levels? If yes -> we done
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> New aspect ratio varies from the old one by this much:\n`,"color: #aaf","old Ar", lastAr.ar, "current ar", trueAr, "arDiff (absolute):",arDiff,"ar diff (relative to new ar)", arDiff_percent);
if (arDiff < trueAr * this.settings.active.arDetect.allowedArVariance){
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> Aspect ratio change denied — diff %: ${arDiff_percent}`, "background: #740; color: #fa2");
return;
}
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> aspect ratio change accepted — diff %: ${arDiff_percent}`, "background: #153; color: #4f9");
}
this.logger.log('info', 'debug', `%c[ArDetect::processAr] <@${this.arid}> Triggering aspect ratio change. New aspect ratio: ${trueAr}`, _ard_console_change);
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: trueAr}, {type: AspectRatio.Automatic, ratio: trueAr});
}
frameCheck(){
if(! this.video){
this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`);
this.conf.destroy();
return;
}
if (!this.blackframeContext) {
this.init();
}
var startTime = performance.now();
let sampleCols = this.sampleCols.slice(0);
//
// [0] blackframe tests (they also determine whether we need fallback mode)
//
try {
this.blackframeContext.drawImage(this.video, 0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height);
this.fallbackMode = false;
} catch (e) {
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] <@${this.arid}> %c[ArDetect::frameCheck] can't draw image on canvas. ${this.canDoFallbackMode ? 'Trying canvas.drawWindow instead' : 'Doing nothing as browser doesn\'t support fallback mode.'}`, "color:#000; backgroud:#f51;", e);
// nothing to see here, really, if fallback mode isn't supported by browser
if (! this.canDoFallbackMode) {
return;
}
if (! this.canvasReadyForDrawWindow()) {
// this means canvas needs to be resized, so we'll just re-run setup with all those new parameters
this.stop();
let newCanvasWidth = window.innerHeight * (this.video.videoWidth / this.video.videoHeight);
let newCanvasHeight = window.innerHeight;
if (this.conf.resizer.videoAlignment === VideoAlignment.Center) {
this.canvasDrawWindowHOffset = Math.round((window.innerWidth - newCanvasWidth) * 0.5);
} else if (this.conf.resizer.videoAlignment === VideoAlignment.Left) {
this.canvasDrawWindowHOffset = 0;
} else {
this.canvasDrawWindowHOffset = window.innerWidth - newCanvasWidth;
}
this.setup(newCanvasWidth, newCanvasHeight);
return;
}
// if this is the case, we'll first draw on canvas, as we'll need intermediate canvas if we want to get a
// smaller sample for blackframe check
this.fallbackMode = true;
try {
this.context.drawWindow(window, this.canvasDrawWindowHOffset, 0, this.canvas.width, this.canvas.height, "rgba(0,0,128,1)");
} catch (e) {
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] can't draw image on canvas with fallback mode either. This error is prolly only temporary.`, "color:#000; backgroud:#f51;", e);
return; // it's prolly just a fluke, so we do nothing special here
}
// draw blackframe sample from our main sample:
this.blackframeContext.drawImage(this.canvas, this.blackframeCanvas.width, this.blackframeCanvas.height)
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] canvas.drawImage seems to have worked`, "color:#000; backgroud:#2f5;");
}
const bfanalysis = this.blackframeTest();
if (bfanalysis.isBlack) {
// we don't do any corrections on frames confirmed black
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Black frame analysis suggests this frame is black or too dark. Doing nothing.`, "color: #fa3", bfanalysis);
return;
}
// if we are in fallback mode, then frame has already been drawn to the main canvas.
// if we are in normal mode though, the frame has yet to be drawn
if (!this.fallbackMode) {
this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
}
const imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height).data;
if (! this.fastLetterboxPresenceTest(imageData, sampleCols) ) {
// Če ne zaznamo letterboxa, kličemo reset. Lahko, da je bilo razmerje stranic popravljeno na roke. Možno je tudi,
// da je letterbox izginil.
// If we don't detect letterbox, we reset aspect ratio to aspect ratio of the video file. The aspect ratio could
// have been corrected manually. It's also possible that letterbox (that was there before) disappeared.
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
this.guardLine.reset();
this.noLetterboxCanvasReset = true;
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Letterbox not detected in fast test. Letterbox is either gone or we manually corrected aspect ratio. Nothing will be done.`, "color: #fa3");
return;
}
// Če preverjamo naprej, potem moramo postaviti to vrednost nazaj na 'false'. V nasprotnem primeru se bo
// css resetiral enkrat na video/pageload namesto vsakič, ko so za nekaj časa obrobe odstranejene
// if we look further we need to reset this value back to false. Otherwise we'll only get CSS reset once
// per video/pageload instead of every time letterbox goes away (this can happen more than once per vid)
this.noLetterboxCanvasReset = false;
// poglejmo, če obrežemo preveč.
// let's check if we're cropping too much
const guardLineOut = this.guardLine.check(imageData, this.fallbackMode);
// če ni padla nobena izmed funkcij, potem se razmerje stranic ni spremenilo
// if both succeed, then aspect ratio hasn't changed.
if (!guardLineOut.imageFail && !guardLineOut.blackbarFail) {
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] guardLine tests were successful. (no imagefail and no blackbarfail)\n`, "color: #afa", guardLineOut);
return;
}
// drugače nadaljujemo, našemu vzorcu stolpcev pa dodamo tiste stolpce, ki so
// kršili blackbar (če obstajajo) ter jih razvrstimo
// otherwise we continue. We add blackbar violations to the list of the cols
// we'll sample and sort them
if (guardLineOut.blackbarFail) {
sampleCols.concat(guardLineOut.offenders).sort((a, b) => a > b);
}
// if we're in fallback mode and blackbar test failed, we restore CSS and quit
// (since the new letterbox edge isn't present in our sample due to technical
// limitations)
if (this.fallbackMode && guardLineOut.blackbarFail) {
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
this.guardLine.reset();
this.noLetterboxCanvasReset = true;
return;
}
// će se razmerje stranic spreminja iz ožjega na širšega, potem najprej poglejmo za prisotnostjo navpičnih črnih obrob.
// če so prisotne navpične obrobe tudi na levi in desni strani, potlej obstaja možnost, da gre za logo na črnem ozadju.
// v tem primeru obstaja nevarnost, da porežemo preveč. Ker obstaja dovolj velika možnost, da bi porezali preveč, rajši
// ne naredimo ničesar.
//
// če je pillarbox zaznan v primeru spremembe iz ožjega na širše razmerje stranice, razmerje povrnemo na privzeto vrednost.
//
// If aspect ratio changes from narrower to wider, we first check for presence of pillarbox. Presence of pillarbox indicates
// a chance of a logo on black background. We could cut easily cut too much. Because there's a somewhat significant chance
// that we will cut too much, we rather avoid doing anything at all. There's gonna be a next chance.
try{
if(guardLineOut.blackbarFail || guardLineOut.imageFail){
if(this.edgeDetector.findBars(imageData, null, EdgeDetectPrimaryDirection.HORIZONTAL).status === 'ar_known'){
if(guardLineOut.blackbarFail){
this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] Detected blackbar violation and pillarbox. Resetting to default aspect ratio.`);
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
this.guardLine.reset();
}
triggerTimeout = this.getTimeout(baseTimeout, startTime);
this.scheduleFrameCheck(triggerTimeout);
return;
}
}
} catch(e) {
this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] something went wrong while checking for pillarbox. Error:\n`, e);
}
// pa poglejmo, kje se končajo črne letvice na vrhu in na dnu videa.
// let's see where black bars end.
this.sampleCols_current = sampleCols.length;
// blackSamples -> {res_top, res_bottom}
var edgePost = this.edgeDetector.findBars(imageData, sampleCols, EdgeDetectPrimaryDirection.VERTICAL, EdgeDetectQuality.IMPROVED, guardLineOut, bfanalysis);
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] edgeDetector returned this\n`, "color: #aaf", edgePost);
if (edgePost.status !== EdgeStatus.AR_KNOWN){
// rob ni bil zaznan, zato ne naredimo ničesar.
// no edge was detected. Let's leave things as they were
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Edge wasn't detected with findBars`, "color: #fa3", edgePost, "EdgeStatus.AR_KNOWN:", EdgeStatus.AR_KNOWN);
return;
}
var newAr = this.calculateArFromEdges(edgePost);
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Triggering aspect ration change! new ar: ${newAr}`, "color: #aaf");
// we also know edges for guardline, so set them.
// we need to be mindful of fallbackMode though
// if edges are okay and not invalid, we also
// allow automatic aspect ratio correction. If edges
// are bogus, we remain aspect ratio unchanged.
try {
if (!this.fallbackMode) {
// throws error if top/bottom are invalid
this.guardLine.setBlackbar({top: edgePost.guardLineTop, bottom: edgePost.guardLineBottom});
} else {
if (this.conf.player.dimensions){
this.guardLine.setBlackbarManual({
top: this.settings.active.arDetect.fallbackMode.noTriggerZonePx,
bottom: this.conf.player.dimensions.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1
},{
top: edgePost.guardLineTop + this.settings.active.arDetect.guardLine.edgeTolerancePx,
bottom: edgePost.guardLineBottom - this.settings.active.arDetect.guardLine.edgeTolerancePx
})
}
}
this.processAr(newAr);
} catch (e) {
// edges weren't gucci, so we'll just reset
// the aspect ratio to defaults
try {
this.guardline.reset();
} catch (e) {
// guardline wasn't gucci either, but we'll just ignore
// that and reset the aspect ratio anyway
}
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
}
}
resetBlackLevel(){
this.blackLevel = this.settings.active.arDetect.blackbar.blackLevel;
}
blackLevelTest_full() {
}
blackframeTest() {
if (this.blackLevel === undefined) {
this.logger.log('info', 'arDetect_verbose', "[ArDetect::blackframeTest] black level undefined, resetting");
this.resetBlackLevel();
}
const rows = this.blackframeCanvas.height;
const cols = this.blackframeCanvas.width;
const pixels = rows * cols;
let cumulative_r = 0, cumulative_g = 0, cumulative_b = 0;
let max_r = 0, max_g = 0, max_b = 0;
let avg_r, avg_g, avg_b;
let var_r = 0, var_g = 0, var_b = 0;
let pixelMax = 0;
let cumulativeValue = 0;
let blackPixelCount = 0;
const bfImageData = this.blackframeContext.getImageData(0, 0, cols, rows).data;
const blackTreshold = this.blackLevel + this.settings.active.arDetect.blackbar.frameTreshold;
// we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists
// with sufficient level of certainty due to small sample resolution, it can still give us some hints for later
let rowMax = new Array(rows).fill(0);
let colMax = new Array(cols).fill(0);
let r, c;
for (let i = 0; i < bfImageData.length; i+= 4) {
pixelMax = Math.max(bfImageData[i], bfImageData[i+1], bfImageData[i+2]);
bfImageData[i+3] = pixelMax;
if (pixelMax < blackTreshold) {
if (pixelMax < this.blackLevel) {
this.blackLevel = pixelMax;
}
blackPixelCount++;
} else {
cumulativeValue += pixelMax;
cumulative_r += bfImageData[i];
cumulative_g += bfImageData[i+1];
cumulative_b += bfImageData[i+2];
max_r = max_r > bfImageData[i] ? max_r : bfImageData[i];
max_g = max_g > bfImageData[i+1] ? max_g : bfImageData[i+1];
max_b = max_b > bfImageData[i+2] ? max_b : bfImageData[i+2];
}
r = ~~(i/rows);
c = i % cols;
if (pixelMax > rowMax[r]) {
rowMax[r] = pixelMax;
}
if (pixelMax > colMax[c]) {
colMax[c] = colMax;
}
}
max_r = 1 / (max_r || 1);
max_g = 1 / (max_g || 1);
max_b = 1 / (max_b || 1);
const imagePixels = pixels - blackPixelCount;
// calculate averages and normalize them
avg_r = (cumulative_r / imagePixels) * max_r;
avg_g = (cumulative_g / imagePixels) * max_g;
avg_b = (cumulative_b / imagePixels) * max_b;
// second pass for color variance
for (let i = 0; i < bfImageData.length; i+= 4) {
if (bfImageData[i+3] >= this.blackLevel) {
var_r += Math.abs(avg_r - bfImageData[i] * max_r);
var_g += Math.abs(avg_g - bfImageData[i+1] * max_g);
var_b += Math.abs(avg_b - bfImageData[i+1] * max_b);
}
}
const hasSufficientVariance = Math.abs(var_r - var_g) / Math.max(var_r, var_g, 1) > this.settings.active.arDetect.blackframe.sufficientColorVariance
|| Math.abs(var_r - var_b) / Math.max(var_r, var_b, 1) > this.settings.active.arDetect.blackframe.sufficientColorVariance
|| Math.abs(var_b - var_g) / Math.max(var_b, var_g, 1) > this.settings.active.arDetect.blackframe.sufficientColorVariance
let isBlack = (blackPixelCount/(cols * rows) > this.settings.active.arDetect.blackframe.blackPixelsCondition);
if (! isBlack) {
if (hasSufficientVariance) {
isBlack = cumulativeValue < this.settings.active.arDetect.blackframe.cumulativeThresholdLax;
} else {
isBlack = cumulativeValue < this.settings.active.arDetect.blackframe.cumulativeThresholdStrict;
}
}
if (Debug.debug) {
return {
isBlack: isBlack,
blackPixelCount: blackPixelCount,
blackPixelRatio: (blackPixelCount/(cols * rows)),
cumulativeValue: cumulativeValue,
hasSufficientVariance: hasSufficientVariance,
blackLevel: this.blackLevel,
variances: {
raw: {
r: var_r, g: var_g, b: var_b
},
relative: {
rg: Math.abs(var_r - var_g) / Math.max(var_r, var_g, 1),
rb: Math.abs(var_r - var_b) / Math.max(var_r, var_b, 1),
gb: Math.abs(var_b - var_g) / Math.max(var_b, var_g, 1),
},
relativePercent: {
rg: Math.abs(var_r - var_g) / Math.max(var_r, var_g, 1) / this.settings.active.arDetect.blackframe.sufficientColorVariance,
rb: Math.abs(var_r - var_b) / Math.max(var_r, var_b, 1) / this.settings.active.arDetect.blackframe.sufficientColorVariance,
gb: Math.abs(var_b - var_g) / Math.max(var_b, var_g, 1) / this.settings.active.arDetect.blackframe.sufficientColorVariance,
},
varianceLimit: this.settings.active.arDetect.blackframe.sufficientColorVariance,
},
cumulativeValuePercent: cumulativeValue / (hasSufficientVariance ? this.settings.active.arDetect.blackframe.cumulativeThresholdLax : this.settings.active.arDetect.blackframe.cumulativeThresholdStrict),
rowMax: rowMax,
colMax: colMax,
};
}
return {
isBlack: isBlack,
rowMax: rowMax,
colMax: colMax,
};
}
fastLetterboxPresenceTest(imageData, sampleCols) {
// fast test to see if aspect ratio is correct.
// returns 'true' if presence of letterbox is possible.
// returns 'false' if we found a non-black edge pixel.
// If we detect anything darker than blackLevel, we modify blackLevel to the new lowest value
const rowOffset = this.canvas.width * (this.canvas.height - 1);
let currentMin = 255, currentMax = 0, colOffset_r, colOffset_g, colOffset_b, colOffset_rb, colOffset_gb, colOffset_bb, blthreshold = this.settings.active.arDetect.blackbar.threshold;
// detect black level. if currentMax comes above blackbar + blackbar threshold, we know we aren't letterboxed
for (var i = 0; i < sampleCols.length; ++i){
colOffset_r = sampleCols[i] << 2;
colOffset_g = colOffset_r + 1;
colOffset_b = colOffset_r + 2;
colOffset_rb = colOffset_r + rowOffset;
colOffset_gb = colOffset_g + rowOffset;
colOffset_bb = colOffset_b + rowOffset;
currentMax = Math.max(
imageData[colOffset_r], imageData[colOffset_g], imageData[colOffset_b],
// imageData[colOffset_rb], imageData[colOffset_gb], imageData[colOffset_bb],
currentMax
);
if (currentMax > this.blackLevel + blthreshold) {
// we search no further
if (currentMin < this.blackLevel) {
this.blackLevel = currentMin;
}
return false;
}
currentMin = Math.min(
currentMax,
currentMin
);
}
if (currentMin < this.blackLevel)
this.blackLevel = currentMin
return true;
}
}
var _ard_console_stop = "background: #000; color: #f41";
var _ard_console_start = "background: #000; color: #00c399";
var _ard_console_change = "background: #000; color: #ff8";
export default ArDetector;

View File

@ -1,129 +0,0 @@
class DebugCanvas {
constructor(ardConf){
this.conf = ardConf;
this.targetWidth = 1280;
this.targetHeight = 720;
this.targetOffsetTop = 1080;
this.targetOffsetLeft = 100;
}
init(canvasSize, canvasPosition, targetCanvasSize) {
console.log("initiating DebugCanvas")
var body = document.getElementsByTagName('body')[0];
if(!canvasPosition){
canvasPosition = {
top: 1200,
left: 800
}
}
if(!this.canvas){
this.canvas = document.createElement("canvas");
body.appendChild(this.canvas);
}
if(targetCanvasSize){
this.targetWidth = targetCanvasSize.width;
this.targetHeight = targetCanvasSize.height;
}
this.canvas.style.position = "absolute";
this.canvas.style.left = `${canvasPosition.left}px`;
this.canvas.style.top = `${canvasPosition.top}px`;
this.canvas.style.zIndex = 10002;
// this.canvas.id = "uw_debug_canvas";
this.context = this.canvas.getContext("2d");
this.canvas.width = canvasSize.width;
this.canvas.height = canvasSize.height;
this.calculateCanvasZoom();
console.log("debug canvas is:", this.canvas, "context:", this.context)
}
calculateCanvasZoom(){
var canvasZoom = this.targetWidth / this.canvas.width;
var translateX = (this.canvas.width - this.targetWidth)/2;
var translateY = (this.canvas.height - this.targetHeight)/2;
this.canvas.style.transform = `scale(${canvasZoom},${canvasZoom}) translateX(${translateX}px) translateY(${translateY}px)`;
}
destroy(){
if(this.canvas)
this.canvas.remove();
}
setBuffer(buffer) {
// this.imageBuffer = buffer.splice(0);
this.imageBuffer = new Uint8ClampedArray(buffer);
}
trace(arrayIndex, colorClass) {
this.imageBuffer[arrayIndex ] = colorClass.colorRgb[0];
this.imageBuffer[arrayIndex+1] = colorClass.colorRgb[1];
this.imageBuffer[arrayIndex+2] = colorClass.colorRgb[2];
}
update() {
var start = performance.now();
try{
if(this.context && this.imageBuffer instanceof Uint8ClampedArray){
try{
var idata = new ImageData(this.imageBuffer, this.canvas.width, this.canvas.height);
} catch (ee) {
console.log("[DebugCanvas.js::update] can't create image data. Trying to correct canvas size. Error was:", ee);
this.canvas.width = this.conf.canvas.width;
this.canvas.height = this.conf.canvas.height;
this.calculateCanvasZoom();
// this.context = this.canvas.getContext("2d");
var idata = new ImageData(this.imageBuffer, this.canvas.width, this.canvas.height);
}
this.putImageData(this.context, idata, 0, 0, 0, 0, this.canvas.width, this.canvas.height);
}
} catch(e) {
console.log("[DebugCanvas.js::update] updating canvas failed.", e);
}
console.log("[DebugCanvas.js::update] update took", (performance.now() - start), "ms.");
}
putImageData(ctx, imageData, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
var data = imageData.data;
var height = imageData.height;
var width = imageData.width;
dirtyX = dirtyX || 0;
dirtyY = dirtyY || 0;
dirtyWidth = dirtyWidth !== undefined? dirtyWidth: width;
dirtyHeight = dirtyHeight !== undefined? dirtyHeight: height;
var limitBottom = Math.min(dirtyHeight, height);
var limitRight = Math.min(dirtyWidth, width);
for (var y = dirtyY; y < limitBottom; y++) {
for (var x = dirtyX; x < limitRight; x++) {
var pos = y * width + x;
ctx.fillStyle = 'rgba(' + data[pos*4+0]
+ ',' + data[pos*4+1]
+ ',' + data[pos*4+2]
+ ',' + (data[pos*4+3]/255) + ')';
ctx.fillRect(x + dx, y + dy, 1, 1);
}
}
}
}
DebugCanvasClasses = {
VIOLATION: {color: '#ff0000', colorRgb: [255, 0, 0], text: 'violation (general)'},
WARN: {color: '#d0d039', colorRgb: [208, 208, 57], text: 'lesser violation (general)'},
GUARDLINE_BLACKBAR: {color: '#3333FF', colorRgb: [51, 51, 255], text: 'guardline/blackbar (expected value)'},
GUARDLINE_IMAGE: {color: '#000088', colorRgb: [0, 0, 136], text: 'guardline/image (expected value)'},
EDGEDETECT_ONBLACK: {color: '#444444', colorRgb: [68, 68, 68], text: 'edge detect - perpendicular (to edge)'},
EDGEDETECT_CANDIDATE: {color: '#FFFFFF', colorRgb: [255, 255, 255], text: 'edge detect - edge candidate'},
EDGEDETECT_CANDIDATE_SECONDARY: {color: '#OOOOOO', colorRgb: [0, 0, 0], text: 'edge detect - edge candidate, but for when candidate is really bright'},
EDGEDETECT_BLACKBAR: {color: '#07ac93', colorRgb: [7, 172, 147], text: 'edge detect - parallel, black test'},
EDGEDETECT_IMAGE: {color: '#046c5c', colorRgb: [4, 108, 92], text: 'edge detect - parallel, image test'}
}

View File

@ -1,305 +0,0 @@
import Debug from '../../conf/Debug';
class GuardLine {
// ardConf — reference to ArDetector that has current GuardLine instance
constructor(ardConf){
this.blackbar = {top: undefined, bottom: undefined};
this.imageBar = {top: undefined, bottom: undefined};
this.conf = ardConf;
this.settings = ardConf.settings;
}
reset() {
this.blackbar = {top: undefined, bottom: undefined};
this.imageBar = {top: undefined, bottom: undefined};
}
setBlackbarManual(blackbarConf, imagebarConf){
// ni lepo uporabljat tega, ampak pri fallback mode nastavljamo blackbar stuff na roke
// it's not nice to use this, but we're setting these values manually in fallbackMode
if (blackbarConf) {
this.blackbar = blackbarConf;
}
if (imagebarConf) {
this.imageBar = imagebarConf;
}
}
setBlackbar(bbconf){
var bbTop = bbconf.top - this.settings.active.arDetect.guardLine.edgeTolerancePx;
var bbBottom = bbconf.bottom + this.settings.active.arDetect.guardLine.edgeTolerancePx;
// to odstrani vse neveljavne nastavitve in vse možnosti, ki niso smiselne
// this removes any configs with invalid values or values that dont make sense
if (bbTop < 0 || bbBottom >= this.conf.canvas.height ){
throw "INVALID_SETTINGS_IN_GUARDLINE"
}
this.blackbar = {
top: bbTop,
bottom: bbBottom
}
this.imageBar = {
top: bbconf.top + 1 + this.settings.active.arDetect.guardLine.edgeTolerancePx,
bottom: bbconf.bottom - 1 - this.settings.active.arDetect.guardLine.edgeTolerancePx
}
}
check(image, fallbackMode){
// izračunaj enkrat in shrani na objekt
// calculate once and save object-instance-wide
this.blackbarThreshold = this.conf.blackLevel + this.settings.active.arDetect.blackbar.threshold;
this.imageThreshold = this.blackbarThreshold + this.settings.active.arDetect.blackbar.imageThreshold;
// dejansko testiranje
// actual checks
var guardLineResult = this.guardLineCheck(image, fallbackMode);
// Zaznali smo kršitev črnega dela, zato nam ni treba preveriti, ali je slika
// prisotna. Vemo namreč, da se je razmerje stranic zmanjšalo.
//
// blackbar violation detected. We don't even need to check for presence of image
// as aspect ratio just decreased
if(! guardLineResult.success) {
return {
blackbarFail: true,
offenders: guardLineResult.offenders,
imageFail: false
}
}
var imageCheckResult = this.imageCheck(image, fallbackMode);
return {
blackbarFail: false,
imageFail: ! imageCheckResult.success
}
}
// don't use methods below this line outside this class
guardLineCheck(image, fallbackMode){
// this test tests for whether we crop too aggressively
// if this test is passed, then aspect ratio probably didn't change from wider to narrower. However, further
// checks are needed to determine whether aspect ratio got wider.
// if this test fails, it returns a list of offending points.
// if the upper edge is null, then edge hasn't been detected before. This test is pointless, therefore it
// should succeed by default. Also need to check bottom, for cases where only one edge is known
if(! fallbackMode && (! this.blackbar.top || ! this.blackbar.bottom)) {
return { success: true };
}
var offset = parseInt(this.conf.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
var offenders = [];
var offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index.
// TODO: implement logo check.
// preglejmo obe vrstici
// check both rows
if(! fallbackMode){
var edge_upper = this.blackbar.top;
var edge_lower = this.blackbar.bottom;
}
else{
// fallback mode is a bit different
edge_upper = 0;
edge_lower = this.conf.canvas.height - 1;
}
var rowStart, rowEnd;
// <<<=======| checking upper row |========>>>
rowStart = ((edge_upper * this.conf.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
} else {
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
}
// <<<=======| checking lower row |========>>>
rowStart = ((edge_lower * this.conf.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
} else {
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
}
// če nismo našli nobenih prekrškarjev, vrnemo uspeh. Drugače vrnemo seznam prekrškarjev
// vrnemo tabelo, ki vsebuje sredinsko točko vsakega prekrškarja (x + width*0.5)
//
// if we haven't found any offenders, we return success. Else we return list of offenders
// we return array of middle points of offenders (x + (width * 0.5) for every offender)
if(offenderCount == -1){
return {success: true};
}
var ret = new Array(offenders.length);
for(var o in offenders){
ret[o] = offenders[o].x + (offenders[o].width * 0.25);
}
return {success: false, offenders: ret};
}
imageCheck(image){
if(!this.imageBar.top || !this.imageBar.bottom)
return { success: false };
var offset = (this.conf.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
// TODO: implement logo check.
// preglejmo obe vrstici - tukaj po pravilih ne bi smeli iti prek mej platna. ne rabimo preverjati
// check both rows - by the rules and definitions, we shouldn't go out of bounds here. no need to check, then
// if(fallbackMode){
// var edge_upper = this.settings.active.arDetect.fallbackMode.noTriggerZonePx;
// var edge_lower = this.conf.canvas.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1;
// }
// else{
var edge_upper = this.imageBar.top;
var edge_lower = this.imageBar.bottom;
// }
// koliko pikslov rabimo zaznati, da je ta funkcija uspe. Tu dovoljujemo tudi, da so vsi piksli na enem
// robu (eden izmed robov je lahko v celoti črn)
// how many non-black pixels we need to consider this check a success. We only need to detect enough pixels
// on one edge (one of the edges can be black as long as both aren't)
var successThreshold = (this.conf.canvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold);
var rowStart, rowEnd;
// <<<=======| checking upper row |========>>>
rowStart = ((edge_upper * this.conf.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
var res = false;
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
if(res)
return {success: true};
// <<<=======| checking lower row |========>>>
rowStart = ((edge_lower * this.conf.canvas.width) << 2) + offset;
// rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
return {success: res};
}
// pomožne metode
// helper methods
_gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount){
var firstOffender = -1;
for(var i = rowStart; i < rowEnd; i+=4){
// we track sections that go over what's supposed to be a black line, so we can suggest more
// columns to sample
if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
if(firstOffender < 0){
firstOffender = (i - rowStart) >> 2;
offenderCount++;
offenders.push({x: firstOffender, width: 1});
}
else{
offenders[offenderCount].width++
}
}
else{
// is that a black pixel again? Let's reset the 'first offender'
firstOffender = -1;
}
}
return offenderCount;
}
_gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){
var firstOffender = -1;
for(var i = rowStart; i < rowEnd; i+=4){
// we track sections that go over what's supposed to be a black line, so we can suggest more
// columns to sample
if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION);
if(firstOffender < 0){
firstOffender = (i - rowStart) >> 2;
offenderCount++;
offenders.push({x: firstOffender, width: 1});
}
else{
offenders[offenderCount].width++
}
}
else{
this.conf.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR);
// is that a black pixel again? Let's reset the 'first offender'
firstOffender = -1;
}
}
return offenderCount;
}
_ti_checkRow(image, rowStart, rowEnd, successThreshold) {
for(var i = rowStart; i < rowEnd; i+=4){
if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
if(successThreshold --<= 0){
return true;
}
}
}
return false;
}
_ti_debugCheckRow(image, rowStart, rowEnd, successThreshold) {
for(var i = rowStart; i < rowEnd; i+=4){
if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
this.conf.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_IMAGE);
if(successThreshold --<= 0){
return true;
}
} else {
this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN);
}
}
return false;
}
}
export default GuardLine;

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
var EdgeDetectPrimaryDirection = Object.freeze({
VERTICAL: 0,
HORIZONTAL: 1
});
export default EdgeDetectPrimaryDirection;

View File

@ -1,6 +0,0 @@
var EdgeDetectQuality = Object.freeze({
FAST: 0,
IMPROVED: 1
});
export default EdgeDetectQuality;

View File

@ -1,6 +0,0 @@
var EdgeStatus = Object.freeze({
AR_UNKNOWN: 0,
AR_KNOWN: 1,
});
export default EdgeStatus;

View File

@ -1,35 +0,0 @@
import Debug from '../../conf/Debug';
import BrowserDetect from '../../conf/BrowserDetect';
class Comms {
static async sendMessage(message){
if(BrowserDetect.firefox){
return browser.runtime.sendMessage(message)
} else {
return new Promise((resolve, reject) => {
try{
if(BrowserDetect.edge){
browser.runtime.sendMessage(message, function(response){
var r = response;
resolve(r);
});
} else {
chrome.runtime.sendMessage(message, function(response){
// Chrome/js shittiness mitigation — remove this line and an empty array will be returned
var r = response;
resolve(r);
return true;
});
}
}
catch(e){
reject(e);
}
});
}
}
}
export default Comms;

View File

@ -1,178 +0,0 @@
import Debug from '../../conf/Debug';
import BrowserDetect from '../../conf/BrowserDetect';
class CommsClient {
constructor(name, settings, logger) {
this.logger = logger;
if (BrowserDetect.firefox) {
this.port = browser.runtime.connect({name: name});
} else if (BrowserDetect.chrome) {
this.port = chrome.runtime.connect({name: name});
} else if (BrowserDetect.edge) {
this.port = browser.runtime.connect({name: name})
}
var ths = this;
this._listener = m => ths.processReceivedMessage(m);
this.port.onMessage.addListener(this._listener);
this.settings = settings;
this.pageInfo = undefined;
this.commsId = (Math.random() * 20).toFixed(0);
}
destroy() {
this.pageInfo = null;
this.settings = null;
if (!BrowserDetect.edge) { // edge is a very special browser made by outright morons.
this.port.onMessage.removeListener(this._listener);
}
}
setPageInfo(pageInfo){
this.pageInfo = pageInfo;
this.logger.log('info', 'debug', `[CommsClient::setPageInfo] <${this.commsId}>`, "SETTING PAGEINFO —", this.pageInfo, this)
var ths = this;
this._listener = m => ths.processReceivedMessage(m);
if (!BrowserDetect.edge) {
this.port.onMessage.removeListener(this._listener);
}
this.port.onMessage.addListener(this._listener);
}
processReceivedMessage(message){
this.logger.log('info', 'comms', `[CommsClient.js::processMessage] <${this.commsId}> Received message from background script!`, message);
if (!this.pageInfo || !this.settings.active) {
this.logger.log('info', 'comms', `[CommsClient.js::processMessage] <${this.commsId}> this.pageInfo (or settings) not defined. Extension is probably disabled for this site.\npageInfo:`, this.pageInfo,
"\nsettings.active:", this.settings.active,
"\nnobj:", this
);
return;
}
if (message.cmd === 'get-current-zoom') {
this.pageInfo.requestCurrentZoom();
}
if (message.cmd === "set-ar") {
this.pageInfo.setAr({type: message.arg, ratio: message.customArg}, message.playing);
} else if (message.cmd === 'set-alignment') {
this.pageInfo.setVideoAlignment(message.arg, message.playing);
this.pageInfo.restoreAr();
} else if (message.cmd === "set-stretch") {
this.pageInfo.setStretchMode(message.arg, message.playing);
} else if (message.cmd === 'set-keyboard') {
this.pageInfo.setKeyboardShortcutsEnabled(message.arg)
} else if (message.cmd === "autoar-start") {
if (message.enabled !== false) {
this.pageInfo.initArDetection(message.playing);
this.pageInfo.startArDetection(message.playing);
} else {
this.pageInfo.stopArDetection(message.playing);
}
} else if (message.cmd === "pause-processing") {
this.pageInfo.pauseProcessing(message.playing);
} else if (message.cmd === "resume-processing") {
// todo: autoArStatus
this.pageInfo.resumeProcessing(message.autoArStatus, message.playing);
} else if (message.cmd === 'set-zoom') {
this.pageInfo.setZoom(message.arg, true, message.playing);
} else if (message.cmd === 'change-zoom') {
this.pageInfo.zoomStep(message.arg, message.playing);
} else if (message.cmd === 'mark-player') {
this.pageInfo.markPlayer(message.name, message.color);
} else if (message.cmd === 'unmark-player') {
this.pageInfo.unmarkPlayer();
} else if (message.cmd === 'autoar-set-manual-tick') {
this.pageInfo.setManualTick(message.arg);
} else if (message.cmd === 'autoar-tick') {
this.pageInfo.tick();
}
}
async sleep(n){
return new Promise( (resolve, reject) => setTimeout(resolve, n) );
}
async sendMessage_nonpersistent(message){
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
if(BrowserDetect.firefox){
return browser.runtime.sendMessage(message)
} else {
return new Promise((resolve, reject) => {
try{
if(BrowserDetect.edge){
browser.runtime.sendMessage(message, function(response){
var r = response;
resolve(r);
});
} else {
chrome.runtime.sendMessage(message, function(response){
// Chrome/js shittiness mitigation — remove this line and an empty array will be returned
var r = response;
resolve(r);
return true;
});
}
}
catch(e){
reject(e);
}
return true;
});
}
}
async requestSettings(){
this.logger.log('info', 'comms', "%c[CommsClient::requestSettings] sending request for congif!", "background: #11D; color: #aad");
var response = await this.sendMessage_nonpersistent({cmd: 'get-config'});
this.logger.log('info', 'comms', "%c[CommsClient::requestSettings] received settings response!", "background: #11D; color: #aad", response);
if(! response || response.extensionConf){
return Promise.resolve(false);
}
this.settings.active = JSON.parse(response.extensionConf);
return Promise.resolve(true);
}
async sendMessage(message) {
await this.sendMessage_nonpersistent(message);
}
registerVideo(){
this.logger.log('info', 'comms', `[CommsClient::registerVideo] <${this.commsId}>`, "Registering video for current page.");
if (this.pageInfo) {
if (this.pageInfo.hasVideo()) {
this.port.postMessage({cmd: "has-video"});
}
} else {
// this.port.postMessage({cmd: "has-video"});
}
}
sendPerformanceUpdate(message){
this.port.postMessage({cmd: 'performance-update', message: message});
}
unregisterVideo(){
this.logger.log('info', 'comms', `[CommsClient::unregisterVideo] <${this.commsId}>`, "Unregistering video for current page.");
this.port.postMessage({cmd: "noVideo"}); // ayymd
}
announceZoom(scale){
this.port.postMessage({cmd: "announce-zoom", zoom: scale});
this.registerVideo()
}
}
export default CommsClient;

View File

@ -1,236 +0,0 @@
import Debug from '../../conf/Debug';
import BrowserDetect from '../../conf/BrowserDetect';
class CommsServer {
constructor(server) {
this.server = server;
this.logger = server.logger;
this.settings = server.settings;
this.ports = [];
var ths = this;
if (BrowserDetect.firefox) {
browser.runtime.onConnect.addListener(p => ths.onConnect(p));
browser.runtime.onMessage.addListener((m, sender) => ths.processReceivedMessage_nonpersistent(m, sender));
} else {
chrome.runtime.onConnect.addListener(p => ths.onConnect(p));
chrome.runtime.onMessage.addListener((m, sender, callback) => ths.processReceivedMessage_nonpersistent(m, sender, callback));
}
}
async getCurrentTabHostname() {
const activeTab = await this._getActiveTab();
const url = activeTab[0].url;
var hostname;
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
hostname = url.split('/')[2];
}
else {
hostname = url.split('/')[0];
}
hostname = hostname.split(':')[0]; //find & remove port number
hostname = hostname.split('?')[0]; //find & remove "?"
return hostname;
}
sendToAll(message){
for(var p of this.ports){
for(var frame in p){
p[frame].postMessage(message);
}
}
}
async _getActiveTab() {
if (BrowserDetect.firefox) {
return await browser.tabs.query({currentWindow: true, active: true});
} else {
return await new Promise( (resolve, reject) => {
chrome.tabs.query({lastFocusedWindow: true, active: true}, function (res) {
resolve(res);
return true;
});
});
}
}
async sendToFrame(message, tab, frame) {
this.logger.log('info', 'CommsServer', `%c[CommsServer::sendToFrame] attempting to send message to tab ${tab}, frame ${frame}`, "background: #dda; color: #11D", message);
if (isNaN(tab)) {
if (tab === '__playing') {
message['playing'] = true;
this.sendToAll(message);
return;
} else if (tab === '__all') {
this.sendToAll(message);
return;
}
[tab, frame] = tab.split('-')
}
this.logger.log('info', 'CommsServer', `%c[CommsServer::sendToFrame] attempting to send message to tab ${tab}, frame ${frame}`, "background: #dda; color: #11D", message);
try {
this.ports[tab][frame].postMessage(message);
} catch (e) {
this.logger.log('error', 'CommsServer', `%c[CommsServer::sendToFrame] Sending message failed. Reason:`, "background: #dda; color: #11D", e);
}
}
async sendToActive(message) {
this.logger.log('info', 'CommsServer', "%c[CommsServer::sendToActive] trying to send a message to active tab. Message:", "background: #dda; color: #11D", message);
var tabs = await this._getActiveTab();
this.logger.log('info', 'CommsServer', "[CommsServer::_sendToActive] currently active tab(s)?", tabs);
for (var key in this.ports[tabs[0].id]) {
this.logger.log('info', 'CommsServer', "key?", key, this.ports[tabs[0].id]);
}
for (var key in this.ports[tabs[0].id]) {
this.ports[tabs[0].id][key].postMessage(message);
}
}
onConnect(port){
var ths = this;
// poseben primer | special case
if (port.name === 'popup-port') {
this.popupPort = port;
this.popupPort.onMessage.addListener( (m,p) => ths.processReceivedMessage(m,p));
return;
}
var tabId = port.sender.tab.id;
var frameId = port.sender.frameId;
if(! this.ports[tabId]){
this.ports[tabId] = {};
}
this.ports[tabId][frameId] = port;
this.ports[tabId][frameId].onMessage.addListener( (m,p) => ths.processReceivedMessage(m, p));
this.ports[tabId][frameId].onDisconnect.addListener( (p) => {
delete ths.ports[p.sender.tab.id][p.sender.frameId];
if(Object.keys(ths.ports[p.sender.tab.id]).length === 0){
ths.ports[tabId] = undefined;
}
});
}
async processReceivedMessage(message, port){
this.logger.log('info', 'CommsServer', "[CommsServer.js::processReceivedMessage] Received message from popup/content script!", message, "port", port, "\nsettings and server:", this.settings,this.server);
if (message.forwardToContentScript) {
this.logger.log('info', 'CommsServer', "[CommsServer.js::processReceivedMessage] Message has 'forward to content script' flag set. Forwarding message as is. Message:", message);
this.sendToFrame(message, message.targetTab, message.targetFrame);
}
if (message.forwardToAll) {
this.logger.log('info', 'CommsServer', "[CommsServer.js::processReceivedMessage] Message has 'forward to all' flag set. Forwarding message as is. Message:", message);
this.sendToAll(message);
}
if (message.forwardToActive) {
this.logger.log('info', 'CommsServer', "[CommsServer.js::processReceivedMessage] Message has 'forward to active' flag set. Forwarding message as is. Message:", message);
this.sendToActive(message)
}
if (message.cmd === 'announce-zoom') {
// forward off to the popup, no use for this here
try {
this.popupPort.postMessage({cmd: 'set-current-zoom', zoom: message.zoom});
} catch (e) {
// can't forward stuff to popup if it isn't open
}
} else if (message.cmd === 'get-current-zoom') {
this.sendToActive(message);
}
if (message.cmd === 'get-current-site') {
port.postMessage({
cmd: 'set-current-site',
site: this.server.getVideoTab(),
tabHostname: await this.getCurrentTabHostname()
});
}
if (message.cmd === 'popup-set-selected-tab') {
this.server.setSelectedTab(message.selectedMenu, message.selectedSubitem);
}
if (message.cmd === 'get-config') {
this.logger.log('info', 'CommsServer', "CommsServer: received get-config. Active settings?", this.settings.active, "\n(settings:", this.settings, ")");
port.postMessage(
{cmd: "set-config", conf: this.settings.active, site: this.server.currentSite}
);
} else if (message.cmd === 'has-video') {
this.server.registerVideo(port.sender);
} else if (message.cmd === 'noVideo') {
this.server.unregisterVideo(port.sender);
}
}
processReceivedMessage_nonpersistent(message, sender, sendResponse){
this.logger.log('info', 'CommsServer', "%c[CommsServer.js::processMessage_nonpersistent] Received message from background script!", "background-color: #11D; color: #aad", message, sender);
if (message.cmd === 'inject-css') {
this.server.injectCss(message.cssString, sender);
return;
}
if (message.cmd === 'remove-css' || message.cmd === 'eject-css') {
this.server.removeCss(message.cssString, sender);
return;
}
if (message.cmd === 'replace-css') {
this.server.replaceCss(message.oldCssString, message.newCssString, sender);
}
if (message.forwardToContentScript) {
this.logger.log('info', 'CommsServer', "[CommsServer.js::processMessage_nonpersistent] Message has 'forward to content script' flag set. Forwarding message as is. Message:", message);
this.logger.log('info', 'CommsServer', "[CommsServer.js::processMessage_nonpersistent] (btw we probably shouldn't be seeing this. This should prolly happen in persistent connection?");
this.sendToFrame(message, message.targetFrame);
}
if (message.cmd === 'get-config') {
if (BrowserDetect.firefox) {
var ret = {extensionConf: JSON.stringify(this.settings.active)};
this.logger.log('info', 'CommsServer', "%c[CommsServer.js::processMessage_nonpersistent] Returning this:", "background-color: #11D; color: #aad", ret);
Promise.resolve(ret);
} else {
sendResponse({extensionConf: JSON.stringify(this.settings.active)});
return true;
}
} else if (message.cmd === "autoar-enable") {
this.settings.active.sites['@global'].autoar = "blacklist";
this.settings.save();
this.sendToAll({cmd: "reload-settings", sender: "uwbg"})
this.logger.log('info', 'CommsServer', "[uw-bg] autoar set to enabled (blacklist). evidenz:", this.settings.active);
} else if (message.cmd === "autoar-disable") {
this.settings.active.sites['@global'].autoar = "disabled";
if(message.reason){
this.settings.active.arDetect.disabledReason = message.reason;
} else {
this.settings.active.arDetect.disabledReason = 'User disabled';
}
this.settings.save();
this.sendToAll({cmd: 'reload-settings', newConf: this.settings.active});
this.logger.log('info', 'CommsServer', "[uw-bg] autoar set to disabled. evidenz:", this.settings.active);
} else if (message.cmd === "autoar-set-interval") {
this.logger.log('info', 'CommsServer', `[uw-bg] trying to set new interval for autoAr. New interval is, ${message.timeout} ms`);
// set fairly liberal limit
var timeout = message.timeout < 4 ? 4 : message.timeout;
this.settings.active.arDetect.timer_playing = timeout;
this.settings.save();
this.sendToAll({cmd: 'reload-settings', newConf: this.settings.active});
}
}
}
export default CommsServer;

View File

@ -1,556 +0,0 @@
import Debug from '../../conf/Debug';
import VideoData from './VideoData';
import RescanReason from './enums/RescanReason';
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
if(Debug.debug)
console.log("Loading: PageInfo.js");
class PageInfo {
constructor(comms, settings, logger, extensionMode, readOnly = false){
this.logger = logger;
this.hasVideos = false;
this.siteDisabled = false;
this.videos = [];
this.settings = settings;
this.actionHandlerInitQueue = [];
this.lastUrl = window.location.href;
this.extensionMode = extensionMode;
this.readOnly = readOnly;
if (comms){
this.comms = comms;
}
// request inject css immediately
try {
const playerStyleString = this.settings.active.sites[window.location.host].css.replace('\\n', '');
this.comms.sendMessage({
cmd: 'inject-css',
cssString: playerStyleString
});
} catch (e) {
// do nothing. It's ok if there's no special settings for the player element
}
this.rescan(RescanReason.PERIODIC);
this.scheduleUrlCheck();
this.currentZoomScale = 1;
}
injectCss(cssString) {
this.comms.sendMessage({
cmd: 'inject-css',
cssString: cssString
});
}
ejectCss(cssString) {
this.comms.sendMessage({
cmd: 'eject-css',
cssString: cssString
});
}
replaceCss(oldCssString, newCssString) {
this.comms.sendMessage({
cmd: 'replace-css',
newCssString,
oldCssString
});
}
destroy() {
this.logger.log('info', ['debug', 'init'], "[PageInfo::destroy] destroying all videos!")
if(this.rescanTimer){
clearTimeout(this.rescanTimer);
}
for (var video of this.videos) {
try {
this.comms.unregisterVideo(video.id)
video.destroy();
} catch (e) {
this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e);
}
}
try {
playerStyleString = this.settings.active.sites[window.location.host].css;
if (playerStyleString) {
this.comms.sendMessage({
cmd: 'remove-css',
cssString: playerStyleString
});
}
} catch (e) {
// do nothing. It's ok if there's no special settings for the player element
}
}
reset() {
for(var video of this.videos) {
video.destroy();
}
this.rescan(RescanReason.MANUAL);
}
initMouseActionHandler(videoData) {
if (this.actionHandler) {
this.actionHandler.registerHandleMouse(videoData);
} else {
this.actionHandlerInitQueue.push(videoData);
}
}
setActionHandler(actionHandler) {
this.actionHandler = actionHandler;
for (var item of this.actionHandlerInitQueue) {
this.actionHandler.registerHandleMouse(item);
}
this.actionHandlerInitQueue = [];
}
getVideos(host) {
if (this.settings.active.sites[host]
&& this.settings.active.sites[host].DOM
&& this.settings.active.sites[host].DOM.video
&& this.settings.active.sites[host].DOM.video.manual
&& this.settings.active.sites[host].DOM.video.querySelector){
const videos = document.querySelectorAll(this.settings.active.sites[host].DOM.video.querySelector);
if (videos.length) {
return videos;
}
}
return document.getElementsByTagName('video');
}
hasVideo() {
return this.readOnly ? this.hasVideos : this.videos.length;
}
rescan(rescanReason){
const oldVideoCount = this.videos.length;
try{
var vids = this.getVideos(window.location.host);
if(!vids || vids.length == 0){
this.hasVideos = false;
if(rescanReason == RescanReason.PERIODIC){
this.logger.log('info', 'videoRescan', "[PageInfo::rescan] Scheduling normal rescan.")
this.scheduleRescan(RescanReason.PERIODIC);
}
return;
}
// add new videos
this.hasVideos = false;
var videoExists = false;
var video, v;
for (video of vids) {
// če najdemo samo en video z višino in širino, to pomeni, da imamo na strani veljavne videe
// če trenutni video nima definiranih teh vrednostih, preskočimo vse nadaljnja preverjanja
// <===[:::::::]===>
// if we find even a single video with width and height, that means the page has valid videos
// if video lacks either of the two properties, we skip all further checks cos pointless
if(video.offsetWidth && video.offsetHeight){
this.hasVideos = true;
if (this.readOnly) {
// in lite mode, we're done. This is all the info we want, but we want to actually start doing
// things that interfere with the website. We still want to be runnig a rescan, tho.
if(rescanReason == RescanReason.PERIODIC){
this.scheduleRescan(RescanReason.PERIODIC);
}
return;
}
} else {
continue;
}
videoExists = false;
for (v of this.videos) {
if (v.destroyed) {
continue; //TODO: if destroyed video is same as current video, copy aspect ratio settings to current video
}
if (v.video == video) {
videoExists = true;
break;
}
}
if (videoExists) {
continue;
} else {
this.logger.log('info', 'videoRescan', "[PageInfo::rescan] found new video candidate:", video, "NOTE:: Video initialization starts here:\n--------------------------------\n")
try {
v = new VideoData(video, this.settings, this);
if (!v.invalid) {
v.initArDetection();
} else {
this.logger.log('error', 'debug', 'Video is invalid. Aard not started.', video);
}
this.videos.push(v);
} catch (e) {
this.logger.log('error', 'debug', "rescan error: failed to initialize videoData. Skipping this video.",e);
}
this.logger.log('info', 'videoRescan', "END VIDEO INITIALIZATION\n\n\n-------------------------------------\nvideos[] is now this:", this.videos,"\n\n\n\n\n\n\n\n")
}
}
this.removeDestroyed();
// če smo ostali brez videev, potem odregistriraj stran.
// če nismo ostali brez videev, potem registriraj stran.
//
// if we're left withotu videos on the current page, we unregister the page.
// if we have videos, we call register.
if (this.comms) {
if (this.videos.length != oldVideoCount) { // only if number of videos changed, tho
if (this.videos.length > 0) {
this.comms.registerVideo({host: window.location.host, location: window.location});
} else {
this.comms.unregisterVideo({host: window.location.host, location: window.location});
}
}
}
} catch(e) {
// če pride do zajeba, potem lahko domnevamo da na strani ni nobenega videa. Uničimo vse objekte videoData
// da preprečimo večkratno inicializacijo. Če smo se z našim ugibom zmotili, potem se bodo vsi videi ponovno
// našli ob naslednjem preiskovanju
//
// if we encounter a fuckup, we can assume that no videos were found on the page. We destroy all videoData
// objects to prevent multiple initalization (which happened, but I don't know why). No biggie if we destroyed
// videoData objects in error — they'll be back in the next rescan
this.logger.log('error', 'debug', "rescan error: — destroying all videoData objects",e);
for (const v of this.videos) {
v.destroy();
}
return;
}
if(rescanReason == RescanReason.PERIODIC){
this.scheduleRescan(RescanReason.PERIODIC);
}
}
removeDestroyed(){
this.videos = this.videos.filter( vid => vid.destroyed === false);
}
scheduleRescan(rescanReason){
if(rescanReason != RescanReason.PERIODIC){
this.rescan(rescanReason);
return;
}
try{
if(this.rescanTimer){
clearTimeout(this.rescanTimer);
}
var ths = this;
this.rescanTimer = setTimeout(function(rescanReason){
ths.rescanTimer = null;
ths.rescan(rescanReason);
ths = null;
}, this.settings.active.pageInfo.timeouts.rescan, RescanReason.PERIODIC)
} catch(e) {
this.logger.log('error', 'debug', "[PageInfo::scheduleRescan] scheduling rescan failed. Here's why:",e)
}
}
scheduleUrlCheck() {
try{
if(this.urlCheckTimer){
clearTimeout(this.urlCheckTimer);
}
var ths = this;
this.urlCheckTimer = setTimeout(function(){
ths.urlCheckTimer = null;
ths.ghettoUrlCheck();
ths = null;
}, this.settings.active.pageInfo.timeouts.urlCheck)
} catch(e){
this.logger.log('error', 'debug', "[PageInfo::scheduleUrlCheck] scheduling URL check failed. Here's why:",e)
}
}
ghettoUrlCheck() {
if (this.lastUrl != window.location.href){
this.logger.log('error', 'videoRescan', "[PageInfo::ghettoUrlCheck] URL has changed. Triggering a rescan!");
this.rescan(RescanReason.URL_CHANGE);
this.lastUrl = window.location.href;
}
this.scheduleUrlCheck();
}
initArDetection(playingOnly){
if (playingOnly) {
for(var vd of this.videos){
if(vd.isPlaying()) {
vd.initArDetection();
}
}
return;
} else {
for(var vd of this.videos){
vd.initArDetection();
}
}
}
// to je treba klicat ob menjavi zavihkov
// these need to be called on tab switch
pauseProcessing(playingOnly){
if (playingOnly) {
for(var vd of this.videos){
if (vd.isPlaying()) {
vd.pause();
}
}
} else {
for(var vd of this.videos){
vd.pause();
}
}
}
resumeProcessing(resumeAutoar = false, playingOnly = false){
if (playingOnly) {
for(var vd of this.videos){
if (vd.isPlaying()) {
vd.resume();
if(resumeAutoar){
vd.resumeAutoAr();
}
}
}
} else {
for(var vd of this.videos){
vd.resume();
if(resumeAutoar){
vd.resumeAutoAr();
}
}
}
}
startArDetection(playingOnly){
if (Debug.debug) {
this.logger.log('info', 'debug', '[PageInfo::startArDetection()] starting automatic ar detection!')
}
if (playingOnly) {
for(var vd of this.videos){
if (video.isPlaying()) {
vd.startArDetection();
}
}
} else {
for(var vd of this.videos){
vd.startArDetection();
}
}
}
stopArDetection(playingOnly){
if (playingOnly) {
for(var vd of this.videos){
if (vd.isPlaying()) {
vd.stopArDetection();
}
}
} else {
for(var vd of this.videos){
vd.stopArDetection();
}
}
}
setAr(ar, playingOnly){
this.logger.log('info', 'debug', '[PageInfo::setAr] aspect ratio:', ar, "playing only?", playingOnly)
if (ar.type !== AspectRatio.Automatic) {
this.stopArDetection(playingOnly);
} else {
this.logger.log('info', 'debug', '[PageInfo::setAr] aspect ratio is auto');
try {
for (var vd of this.videos) {
if (!playingOnly || vd.isPlaying()) {
vd.resetLastAr();
}
}
} catch (e) {
this.logger.log('error', 'debug', "???", e);
}
this.initArDetection(playingOnly);
this.startArDetection(playingOnly);
return;
}
// TODO: find a way to only change aspect ratio for one video
if (ar === AspectRatio.Reset) {
for (var vd of this.videos) {
if (!playingOnly || vd.isPlaying()) {
vd.resetAr();
}
}
} else {
for (var vd of this.videos) {
if (!playingOnly || vd.isPlaying()) {
vd.setAr(ar)
}
}
}
}
setVideoAlignment(videoAlignment, playingOnly) {
if (playingOnly) {
for(var vd of this.videos) {
if (vd.isPlaying()) {
vd.setVideoAlignment(videoAlignment)
}
}
} else {
for(var vd of this.videos) {
vd.setVideoAlignment(videoAlignment)
}
}
}
setPanMode(mode, playingOnly) {
if (playingOnly) {
for(var vd of this.videos) {
if (vd.isPlaying()) {
vd.setPanMode(mode);
}
}
} else {
for(var vd of this.videos) {
vd.setPanMode(mode);
}
}
}
restoreAr(playingOnly) {
if (playingOnly) {
for(var vd of this.videos){
if (vd.isPlaying()) {
vd.restoreAr();
}
}
} else {
for(var vd of this.videos){
vd.restoreAr();
}
}
}
setStretchMode(sm, playingOnly){
// TODO: find a way to only change aspect ratio for one video
if (playingOnly) {
for(var vd of this.videos){
if (vd.isPlaying()) {
vd.setStretchMode(sm)
}
}
} else {
for(var vd of this.videos){
vd.setStretchMode(sm)
}
}
}
setZoom(zoomLevel, no_announce, playingOnly) {
if (playingOnly) {
for(var vd of this.videos) {
if (vd.isPlaying()) {
vd.setZoom(zoomLevel, no_announce);
}
}
} else {
for(var vd of this.videos) {
vd.setZoom(zoomLevel, no_announce);
}
}
}
zoomStep(step, playingOnly) {
for(var vd of this.videos){
if (!playingOnly || vd.isPlaying()) {
vd.zoomStep(step);
}
}
}
markPlayer(name, color) {
for (var vd of this.videos) {
vd.markPlayer(name,color);
}
}
unmarkPlayer() {
for (var vd of this.videos) {
vd.unmarkPlayer();
}
}
announceZoom(scale) {
if (this.announceZoomTimeout) {
clearTimeout(this.announceZoom);
}
this.currentZoomScale = scale;
const ths = this;
this.announceZoomTimeout = setTimeout(() => ths.comms.announceZoom(scale), this.settings.active.zoom.announceDebounce);
}
setManualTick(manualTick) {
for(var vd of this.videos) {
vd.setManualTick();
}
}
tick() {
for(var vd of this.videos) {
vd.tick();
}
}
sendPerformanceUpdate(performanceUpdate) {
if(this.comms) {
this.comms.sendPerformanceUpdate(performanceUpdate);
}
}
requestCurrentZoom() {
this.comms.announceZoom(this.currentZoomScale);
}
setKeyboardShortcutsEnabled(state) {
this.actionHandler.setKeybordLocal(state);
}
}
export default PageInfo;

View File

@ -1,407 +0,0 @@
import Debug from '../../conf/Debug';
import ExtensionMode from '../../../common/enums/extension-mode.enum'
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
if(Debug.debug)
console.log("Loading: PlayerData.js");
/* sprejme <video> tag (element) in seznam imen, ki se lahko pojavijo v razredih oz. id staršev.
// vrne dimenzije predvajalnika (širina, višina)
//
// Na youtube v theater mode je razširitev rahlo pokvarjena. Video tag ostane večji od predvajalnika, ko se zapusti
// celozaslonski način. Ta funkcija skuša to težavo rešiti tako, da poišče element predvajalnika, ki je zavit okoli videa.
//
// Funkcija izkorišča lastnost, da bi načeloma moral biti vsak zunanji element večji od notranjega. Najmanjši element od
// <video> značke pa do korena drevesa bi tako moral biti predvajalnik.
//
// Če je podan seznam imen, potem funkcija vrne dimenzije prvega elementa, ki v id oz. razredu vsebuje katerokoli ime iz seznama
//
// | EN |
//
// accepts <video> tag (element) and list of names that can appear in id or class
// returns player dimensions (width, height)
//
// Theater mode is mildly broken on youtube. <video> tag remains bigger than the player after leaving the fullscreen mode, and
// there's nothing we can do about that. This function aims to solve the problem by finding the player element that's wrapped around
// the <video> tag.
//
// In general, an outer tag should be bigger than the inner tag. Therefore the smallest element between <video> tag and the document
// root should be the player.
//
// If list of names is provided, the function returns dimensions of the first element that contains any name from the list in either
// id or class.
*/
class PlayerData {
constructor(videoData) {
try {
this.logger = videoData.logger;
this.videoData = videoData;
this.video = videoData.video;
this.settings = videoData.settings;
this.extensionMode = videoData.extensionMode;
this.invalid = false;
this.element = this.getPlayer();
this.dimensions = undefined;
this.overlayNode = undefined;
// this happens when we don't find a matching player element
if (!this.element) {
this.invalid = true;
return;
}
if (this.extensionMode === ExtensionMode.Enabled) {
this.checkPlayerSizeChange();
}
this.startChangeDetection();
} catch (e) {
console.error('[Ultrawidify::PlayerData::ctor] There was an error setting up player data. You should be never seeing this message. Error:', e);
this.invalid = true;
}
}
async sleep(timeout) {
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
}
static isFullScreen(){
return ( window.innerHeight == window.screen.height && window.innerWidth == window.screen.width);
}
// player size observer may not be strictly necessary here
onPlayerDimensionsChanged(mutationList, observer, context) {
if (context.checkPlayerSizeChange()) {
context.videoData.resizer.restore();
}
}
start(){
this.startChangeDetection();
}
stop(){
this.halted = true;
this.stopChangeDetection();
}
destroy() {
this.stopChangeDetection();
this.destroyOverlay();
}
startChangeDetection(){
if (this.invalid) {
return;
}
try {
const ths = this;
this.observer = new MutationObserver((m,o) => this.onPlayerDimensionsChanged(m,o,ths));
const observerConf = {
attributes: true,
// attributeFilter: ['style', 'class'],
attributeOldValue: true,
};
this.observer.observe(this.element, observerConf);
} catch (e) {
console.error("failed to set observer",e )
}
// legacy mode still exists, but acts as a fallback for observers and is triggered less
// frequently in order to avoid too many pointless checks
this.legacyChangeDetection();
}
async legacyChangeDetection() {
while (!this.halted) {
await this.sleep(1000);
try {
if (this.checkPlayerSizeChange()) {
this.videoData.resizer.restore();
}
} catch (e) {
console.error('[playerdata::legacycd] this message is pretty high on the list of messages you shouldnt see', e);
}
}
}
stopChangeDetection(){
this.observer.disconnect();
}
makeOverlay() {
if (!this.overlayNode) {
this.destroyOverlay();
}
var overlay = document.createElement('div');
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.position = 'absolute';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.zIndex = '1000000000';
overlay.style.pointerEvents = 'none';
this.overlayNode = overlay;
this.element.appendChild(overlay);
}
destroyOverlay() {
if(this.playerIdElement) {
this.playerIdElement.remove();
this.playerIdElement = undefined;
}
if (this.overlayNode) {
this.overlayNode.remove();
this.overlayNode = undefined;
}
}
markPlayer(name, color) {
if (!this.overlayNode) {
this.makeOverlay();
}
if (this.playerIdElement) {
this.playerIdElement.remove();
}
this.playerIdElement = document.createElement('div');
this.playerIdElement.innerHTML = `<div style="background-color: ${color}; color: #fff; position: absolute; top: 0; left: 0">${name}</div>`;
this.overlayNode.appendChild(this.playerIdElement);
}
unmarkPlayer() {
this.logger.log('info', 'debug', "[PlayerData::unmarkPlayer] unmarking player!")
if (this.playerIdElement) {
this.playerIdElement.remove();
}
this.playerIdElement = undefined;
}
collectionHas(collection, element) {
for (let i = 0, len = collection.length; i < len; i++) {
if (collection[i] == element) {
return true;
}
}
return false;
}
getPlayer() {
const host = window.location.host;
let element = this.video.parentNode;
const videoWidth = this.video.offsetWidth, videoHeight = this.video.offsetHeight;
const elementQ = [];
let scorePenalty = 0;
let score;
try {
if(! element ){
this.logger.log('info', 'debug', "[PlayerDetect::_pd_getPlayer] element is not valid, doing nothing.", element)
if(this.element) {
const ths = this;
}
this.element = undefined;
this.dimensions = undefined;
return;
}
if (this.settings.active.sites[host]
&& this.settings.active.sites[host].DOM
&& this.settings.active.sites[host].DOM.player
&& this.settings.active.sites[host].DOM.player.manual) {
if (this.settings.active.sites[host].DOM.player.useRelativeAncestor
&& this.settings.active.sites[host].DOM.player.videoAncestor) {
let parentsLeft = this.settings.active.sites[host].DOM.player.videoAncestor - 1;
while (parentsLeft --> 0) {
element = element.parentNode;
}
if (element) {
return element;
}
} else if (this.settings.active.sites[host].DOM.player.querySelectors) {
const allSelectors = document.querySelectorAll(this.settings.active.sites[host].DOM.player.querySelectors);
// actually we'll also score this branch in a similar way we score the regular, auto branch
while (element) {
// Let's see how this works
if (this.collectionHas(allSelectors, element)) {
score = 100; // every matching element gets a baseline 100 points
// elements that match the size get a hefty bonus
if ( (element.offsetWidth >= videoWidth && this.equalish(element.offsetHeight, videoHeight, 2))
|| (element.offsetHeight >= videoHeight && this.equalish(element.offsetWidth, videoHeight, 2))) {
score += 75;
}
// elements farther away from the video get a penalty
score -= (scorePenalty++) * 20;
// push the element on the queue/stack:
elementQ.push({
score: score,
element: element,
});
}
element = element.parentNode;
}
if (elementQ.length) {
// return element with biggest score
// if video player has not been found, proceed to automatic detection
return elementQ.sort( (a,b) => b.score - a.score)[0].element;
}
}
}
// try to find element the old fashioned way
while (element){
// odstranimo čudne elemente, ti bi pokvarili zadeve
// remove weird elements, those would break our stuff
if ( element.offsetWidth == 0 || element.offsetHeight == 0){
element = element.parentNode;
continue;
}
// element je player, če je ena stranica enako velika kot video, druga pa večja ali enaka.
// za enakost dovolimo mala odstopanja
// element is player, if one of the sides is as long as the video and the other bigger (or same)
// we allow for tiny variations when checking for equality
if ( (element.offsetWidth >= videoWidth && this.equalish(element.offsetHeight, videoHeight, 2))
|| (element.offsetHeight >= videoHeight && this.equalish(element.offsetWidth, videoHeight, 2))) {
// todo — in case the match is only equalish and not exact, take difference into account when
// calculating score
score = 100;
if (element.id.indexOf('player') !== -1) { // prefer elements with 'player' in id
score += 75;
}
// this has only been observed on steam
if (element.id.indexOf('movie') !== -1) {
score += 75;
}
if (element.classList.toString().indexOf('player') !== -1) { // prefer elements with 'player' in classlist, but a bit less than id
score += 50;
}
score -= scorePenalty++; // prefer elements closer to <video>
elementQ.push({
element: element,
score: score,
});
}
element = element.parentNode;
}
if (elementQ.length) {
// return element with biggest score
return elementQ.sort( (a,b) => b.score - a.score)[0].element;
}
// if no candidates were found, something is obviously very, _very_ wrong.
// we return nothing. Player will be marked as invalid and setup will stop.
// VideoData should check for that before starting anything.
this.logger.log('warn', 'debug', '[PlayerData::getPlayer] no matching player was found for video', this.video, 'Extension cannot work on this site.');
return;
} catch (e) {
this.logger.log('crit', 'debug', '[PlayerData::getPlayer] something went wrong while detecting player:', e, 'Shutting down extension for this page');
}
}
equalish(a,b, tolerance) {
return a > b - tolerance && a < b + tolerance;
}
forceRefreshPlayerElement() {
this.checkPlayerSizeChange();
}
checkPlayerSizeChange(){
// this 'if' is just here for debugging — real code starts later. It's safe to collapse and
// ignore the contents of this if (unless we need to change how logging works)
if (this.logger.canLog('debug')){
if (!this.dimensions) {
} else if (this.dimensions && this.dimensions.fullscreen){
if(! PlayerData.isFullScreen()){
this.logger.log('info', 'debug', "[PlayerDetect] player size changed. reason: exited fullscreen");
}
}
if(! this.element) {
this.logger.log('info', 'playerDetect', "[PlayerDetect] player element isn't defined");
}
if ( this.element && this.dimensions &&
( this.dimensions.width != this.element.offsetWidth ||
this.dimensions.height != this.element.offsetHeight )
) {
this.logger.log('info', 'debug', "[PlayerDetect] player size changed. reason: dimension change. Old dimensions?", this.dimensions.width, this.dimensions.height, "new dimensions:", this.element.offsetWidth, this.element.offsetHeight);
}
}
// if size doesn't match, update & return true
if (!this.dimensions
|| this.dimensions.width != this.element.offsetWidth
|| this.dimensions.height != this.element.offsetHeight ){
const isFullScreen = PlayerData.isFullScreen();
if (isFullScreen) {
this.dimensions = {
width: window.innerWidth,
height: window.innerHeight,
fullscreen: true
}
} else {
this.dimensions = {
width: this.element.offsetWidth,
height: this.element.offsetHeight,
fullscreen: isFullScreen
};
}
return true;
}
return false;
}
checkFullscreenChange() {
const isFs = PlayerData.isFullScreen();
if (this.dimensions) {
if (this.dimensions.fullscreen != isFs) {
this.dimensions = {
fullscreen: isFs,
width: isFs ? screen.width : this.video.offsetWidth,
height: isFs ? screen.height : this.video.offsetHeight
};
return true;
}
return false;
}
this.logger.log('info', 'debug', "[PlayerData::checkFullscreenChange] this.dimensions is not defined. Assuming fs change happened and setting default values.")
this.dimensions = {
fullscreen: isFs,
width: isFs ? screen.width : this.video.offsetWidth,
height: isFs ? screen.height : this.video.offsetHeight
};
return true;
}
}
export default PlayerData;

View File

@ -1,413 +0,0 @@
import Debug from '../../conf/Debug';
import PlayerData from './PlayerData';
import Resizer from '../video-transform/Resizer';
import ArDetector from '../ar-detect/ArDetector';
class VideoData {
constructor(video, settings, pageInfo){
this.vdid = (Math.random()*100).toFixed();
this.logger = pageInfo.logger;
this.arSetupComplete = false;
this.video = video;
this.destroyed = false;
this.settings = settings;
this.pageInfo = pageInfo;
this.extensionMode = pageInfo.extensionMode;
this.userCssClassName = `uw-fuck-you-and-do-what-i-tell-you_${this.vdid}`;
// We only init observers once player is confirmed valid
const observerConf = {
attributes: true,
// attributeFilter: ['style', 'class'],
attributeOldValue: true,
};
// POZOR: VRSTNI RED JE POMEMBEN (arDetect mora bit zadnji)
// NOTE: ORDERING OF OBJ INITIALIZATIONS IS IMPORTANT (arDetect needs to go last)
this.player = new PlayerData(this);
if (this.player.invalid) {
this.invalid = true;
return;
}
const ths = this;
this.observer = new MutationObserver( (m, o) => this.onVideoDimensionsChanged(m, o, ths));
this.observer.observe(video, observerConf);
this.dimensions = {
width: this.video.offsetWidth,
height: this.video.offsetHeight,
};
this.resizer = new Resizer(this);
this.arDetector = new ArDetector(this); // this starts Ar detection. needs optional parameter that prevets ardetdctor from starting
// player dimensions need to be in:
// this.player.dimensions
// apply default align and stretch
this.logger.log('info', 'debug', "%c[VideoData::ctor] Initial resizer reset!", {background: '#afd', color: '#132'});
this.resizer.reset();
this.logger.log('info', ['debug', 'init'], '[VideoData::ctor] Created videoData with vdid', this.vdid, '\nextension mode:', this.extensionMode)
this.pageInfo.initMouseActionHandler(this);
this.video.classList.add(this.userCssClassName); // this also needs to be applied BEFORE we initialize resizer!
// start fallback video/player size detection
this.fallbackChangeDetection();
}
async fallbackChangeDetection() {
while (!this.destroyed && !this.invalid) {
await this.sleep(500);
this.validateVideoOffsets();
}
}
async sleep(timeout) {
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
}
onVideoDimensionsChanged(mutationList, observer, context) {
if (!mutationList || context.video === undefined) { // something's wrong
if (observer && context.video) {
observer.disconnect();
}
return;
}
for (let mutation of mutationList) {
if (mutation.type === 'attributes'
&& mutation.attributeName === 'class'
&& !context.video.classList.contains(this.userCssClassName) ) {
// force the page to include our class in classlist, if the classlist has been removed
// while classList.add() doesn't duplicate classes (does nothing if class is already added),
// we still only need to make sure we're only adding our class to classlist if it has been
// removed. classList.add() will _still_ trigger mutation (even if classlist wouldn't change).
// This is a problem because INFINITE RECURSION TIME, and we _really_ don't want that.
context.video.classList.add(this.userCssClassName);
break;
}
}
// adding player observer taught us that if element size gets triggered by a class, then
// the 'style' attributes don't necessarily trigger. This means we also need to trigger
// restoreAr here, in case video size was changed this way
context.player.forceRefreshPlayerElement();
context.restoreAr();
// sometimes something fucky wucky happens and mutations aren't detected correctly, so we
// try to get around that
setTimeout( () => {
context.validateVideoOffsets();
}, 100);
}
validateVideoOffsets() {
// validate if current video still exists. If not, we destroy current object
try {
if (! document.body.contains(this.video)) {
this.destroy();
return;
}
} catch (e) {
}
// THIS BREAKS PANNING
const cs = window.getComputedStyle(this.video);
const pcs = window.getComputedStyle(this.player.element);
try {
const transformMatrix = cs.transform.split(')')[0].split(',');
const translateX = +transformMatrix[4];
const translateY = +transformMatrix[5];
const vh = +(cs.height.split('px')[0]);
const vw = +(cs.width.split('px')[0]);
const ph = +(pcs.height.split('px')[0]);
const pw = +(pcs.width.split('px')[0]);
// TODO: check & account for panning and alignment
if (this.isWithin(vh, (ph - (translateY / 2)), 2)
&& this.isWithin(vw, (pw - (translateX / 2)), 2)) {
} else {
this.player.forceRefreshPlayerElement();
this.restoreAr();
}
} catch(e) {
// do nothing on fail
}
}
isWithin(a, b, diff) {
return a < b + diff && a > b - diff
}
firstTimeArdInit(){
if(this.destroyed || this.invalid) {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
if(! this.arSetupComplete){
this.arDetector = new ArDetector(this);
}
}
initArDetection() {
if(this.destroyed || this.invalid) {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
if(this.arDetector){
this.arDetector.init();
}
else{
this.arDetector = new ArDetector(this);
this.arDetector.init();
}
}
startArDetection() {
this.logger.log('info', 'debug', "[VideoData::startArDetection] starting AR detection")
if(this.destroyed || this.invalid) {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
if(!this.arDetector) {
this.arDetector.init();
}
this.arDetector.start();
}
rebootArDetection() {
if(this.destroyed || this.invalid) {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
this.arDetector.init();
}
stopArDetection() {
if (this.arDetector) {
this.arDetector.stop();
}
}
destroy() {
this.logger.log('info', ['debug', 'init'], `[VideoData::destroy] <vdid:${this.vdid}> received destroy command`);
if (this.video) {
this.video.classList.remove(this.userCssClassName);
}
this.pause();
this.destroyed = true;
try {
this.arDetector.stop();
this.arDetector.destroy();
} catch (e) {}
this.arDetector = undefined;
try {
this.resizer.destroy();
} catch (e) {}
this.resizer = undefined;
try {
this.player.destroy();
} catch (e) {}
try {
this.observer.disconnect();
} catch (e) {}
this.player = undefined;
this.video = undefined;
}
pause(){
this.paused = true;
if(this.arDetector){
this.arDetector.stop();
}
if(this.player){
this.player.stop();
}
}
resume(){
if(this.destroyed || this.invalid) {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
this.paused = false;
try {
this.resizer.start();
if (this.player) {
this.player.start();
}
} catch (e) {
this.logger.log('error', 'debug', "[VideoData.js::resume] cannot resume for reasons. Will destroy videoData. Error here:", e);
this.destroy();
}
}
resumeAutoAr(){
if(this.arDetector){
this.startArDetection();
}
}
setManualTick(manualTick) {
if(this.arDetector){
this.arDetector.setManualTick(manualTick);
}
}
tick() {
if(this.arDetector){
this.arDetector.tick();
}
}
setLastAr(lastAr){
if (this.invalid) {
return;
}
this.resizer.setLastAr(lastAr);
}
setAr(ar, lastAr){
if (this.invalid) {
return;
}
this.resizer.setAr(ar, lastAr);
}
resetAr() {
if (this.invalid) {
return;
}
this.resizer.reset();
}
resetLastAr() {
if (this.invalid) {
return;
}
this.resizer.setLastAr('original');
}
panHandler(event, forcePan) {
if (this.invalid) {
return;
}
if(this.destroyed) {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
if(!this.resizer) {
this.destroy();
return;
}
this.resizer.panHandler(event, forcePan);
}
setPanMode(mode) {
if (this.invalid) {
return;
}
this.resizer.setPanMode(mode);
}
setVideoAlignment(videoAlignment) {
if (this.invalid) {
return;
}
this.resizer.setVideoAlignment(videoAlignment);
}
restoreAr(){
if (this.invalid) {
return;
}
this.resizer.restore();
}
setStretchMode(stretchMode){
if (this.invalid) {
return;
}
this.resizer.setStretchMode(stretchMode);
}
setZoom(zoomLevel, no_announce){
if (this.invalid) {
return;
}
this.resizer.setZoom(zoomLevel, no_announce);
}
zoomStep(step){
if (this.invalid) {
return;
}
this.resizer.zoomStep(step);
}
announceZoom(scale){
if (this.invalid) {
return;
}
this.pageInfo.announceZoom(scale);
}
markPlayer(name, color) {
if (this.invalid) {
return;
}
if (this.player) {
this.player.markPlayer(name, color)
}
}
unmarkPlayer() {
this.player.unmarkPlayer();
}
isPlaying() {
return this.video && this.video.currentTime > 0 && !this.video.paused && !this.video.ended;
}
checkVideoSizeChange(){
const videoWidth = this.video.offsetWidth;
const videoHeight = this.video.offsetHeight;
// this 'if' is just here for debugging — real code starts later. It's safe to collapse and
// ignore the contents of this if (unless we need to change how logging works)
if (this.logger.canLog('debug')){
if(! this.video) {
this.logger.log('info', 'videoDetect', "[VideoDetect] player element isn't defined");
}
if ( this.video && this.dimensions &&
( this.dimensions.width != videoWidth ||
this.dimensions.height != videoHeight )
) {
this.logger.log('info', 'debug', "[VideoDetect] player size changed. reason: dimension change. Old dimensions?", this.dimensions.width, this.dimensions.height, "new dimensions:", this.video.offsetWidth, this.video.offsetHeight);
}
}
// if size doesn't match, update & return true
if (!this.dimensions
|| this.dimensions.width != videoWidth
|| this.dimensions.height != videoHeight ){
this.dimensions = {
width: videoWidth,
height: videoHeight,
};
return true;
}
return false;
}
}
export default VideoData;

View File

@ -1,7 +0,0 @@
var RescanReason = Object.freeze({
PERIODIC: 0,
URL_CHANGE: 1,
MANUAL: 2
});
export default RescanReason;

View File

@ -1,530 +0,0 @@
import Debug from '../../conf/Debug';
import Scaler from './Scaler';
import Stretcher from './Stretcher';
import Zoom from './Zoom';
import PlayerData from '../video-data/PlayerData';
import ExtensionMode from '../../../common/enums/extension-mode.enum';
import Stretch from '../../../common/enums/stretch.enum';
import VideoAlignment from '../../../common/enums/video-alignment.enum';
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
if(Debug.debug) {
console.log("Loading: Resizer.js");
}
class Resizer {
constructor(videoData) {
this.resizerId = (Math.random(99)*100).toFixed(0);
this.conf = videoData;
this.logger = videoData.logger;
this.video = videoData.video;
this.settings = videoData.settings;
this.extensionMode = videoData.extensionMode;
this.scaler = new Scaler(this.conf);
this.stretcher = new Stretcher(this.conf);
this.zoom = new Zoom(this.conf);
// load up default values
this.correctedVideoDimensions = {};
this.currentCss = {};
this.currentStyleString = "";
this.currentPlayerStyleString = "";
this.currentCssValidFor = {};
this.lastAr = {type: AspectRatio.Initial};
this.videoAlignment = this.settings.getDefaultVideoAlignment(window.location.hostname); // this is initial video alignment
this.destroyed = false;
if (this.settings.active.pan) {
this.canPan = this.settings.active.miscSettings.mousePan.enabled;
} else {
this.canPan = false;
}
this.userCss = '';
this.userCssClassName = videoData.userCssClassName;
}
injectCss(css) {
this.conf.pageInfo.injectCss(css);
}
ejectCss(css) {
this.conf.pageInfo.ejectCss(css);
}
replaceCss(oldCss, newCss) {
this.conf.pageInfo.replaceCss(oldCss, newCss);
}
prepareCss(css) {
return `.${this.userCssClassName} {${css}}`;
}
destroy(){
this.logger.log('info', ['debug', 'init'], `[Resizer::destroy] <rid:${this.resizerId}> received destroy command.`);
this.destroyed = true;
}
calculateRatioForLegacyOptions(ar){
// also present as modeToAr in Scaler.js
if (ar.type !== AspectRatio.FitWidth && ar.type !== AspectRatio.FitHeight && ar.ratio) {
return ar;
}
// Skrbi za "stare" možnosti, kot na primer "na širino zaslona", "na višino zaslona" in "ponastavi".
// Približevanje opuščeno.
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatio.Reset. No zoom tho
var ratioOut;
if (!this.conf.video) {
this.logger.log('info', 'debug', "[Scaler.js::modeToAr] No video??",this.conf.video, "killing videoData");
this.conf.destroy();
return null;
}
if (! this.conf.player.dimensions) {
ratioOut = screen.width / screen.height;
} else {
this.logger.log('info', 'debug', `[Resizer::calculateRatioForLegacyOptions] <rid:${this.resizerId}> Player dimensions:`, this.conf.player.dimensions.width ,'x', this.conf.player.dimensions.height,'aspect ratio:', this.conf.player.dimensions.width / this.conf.player.dimensions.height)
ratioOut = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
}
// POMEMBNO: lastAr je potrebno nastaviti šele po tem, ko kličemo _res_setAr(). _res_setAr() predvideva,
// da želimo nastaviti statično (type: 'static') razmerje stranic — tudi, če funkcijo kličemo tu oz. v ArDetect.
//
// IMPORTANT NOTE: lastAr needs to be set after _res_setAr() is called, as _res_setAr() assumes we're
// setting a static aspect ratio (even if the function is called from here or ArDetect).
var fileAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
if (ar.type === AspectRatio.FitWidth){
ar.ratio = ratioOut > fileAr ? ratioOut : fileAr;
}
else if(ar.type === AspectRatio.FitHeight){
ar.ratio = ratioOut < fileAr ? ratioOut : fileAr;
}
else if(ar.type === AspectRatio.Reset){
this.logger.log('info', 'debug', "[Scaler.js::modeToAr] Using original aspect ratio -", fileAr);
ar.ratio = fileAr;
} else {
return null;
}
return ar;
}
setAr(ar, lastAr){
if (this.destroyed) {
return;
}
this.logger.log('info', 'debug', '[Resizer::setAr] <rid:'+this.resizerId+'> trying to set ar. New ar:', ar)
if (ar == null) {
return;
}
if (ar.type === AspectRatio.Automatic ||
ar.type === AspectRatio.Reset && this.lastAr.type === AspectRatio.Initial) {
// some sites do things that interfere with our site (and aspect ratio setting in general)
// first, we check whether video contains anything we don't like
const siteSettings = this.settings.active.sites[window.location.host];
if (siteSettings && siteSettings.autoarPreventConditions) {
if (siteSettings.autoarPreventConditions.videoStyleString) {
const styleString = (this.video.getAttribute('style') || '').split(';');
if (siteSettings.autoarPreventConditions.videoStyleString.containsProperty) {
const bannedProperties = siteSettings.autoarPreventConditions.videoStyleString.containsProperty;
for (const prop in bannedProperties) {
for (const s of styleString) {
if (s.trim().startsWith(prop)) {
// check if css property has a list of allowed values:
if (bannedProperties[prop].allowedValues) {
const styleValue = s.split(':')[1].trim();
// check if property value is on the list of allowed values
// if it's not, we aren't allowed to start aard
if (bannedProperties[prop].allowedValues.indexOf(styleValue) === -1) {
this.logger.log('error', 'debug', "%c[Resizer::setAr] video style contains forbidden css property/value combo: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
return;
}
} else {
// no allowed values, no problem. We have forbidden property
// and this means aard can't start.
this.logger.log('info', 'debug', "%c[Resizer::setAr] video style contains forbidden css property: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
return;
}
}
}
}
}
}
}
}
if (lastAr) {
this.lastAr = this.calculateRatioForLegacyOptions(lastAr);
ar = this.calculateRatioForLegacyOptions(ar);
} else {
// NOTE: "fitw" "fith" and "reset" should ignore ar.ratio bit, but
// I'm not sure whether they do. Check that.
ar = this.calculateRatioForLegacyOptions(ar);
if (! ar) {
this.logger.log('info', 'resizer', `[Resizer::setAr] <${this.resizerId}> Something wrong with ar or the player. Doing nothing.`);
return;
}
this.lastAr = {type: ar.type, ratio: ar.ratio}
}
if (this.extensionMode === ExtensionMode.Basic && !PlayerData.isFullScreen() && ar.type !== AspectRatio.Reset) {
// don't actually apply or calculate css when using basic mode if not in fullscreen
// ... unless we're resetting the aspect ratio to original
return;
}
if (! this.video) {
this.conf.destroy();
}
// pause AR on basic stretch, unpause when using other modes
// for sine reason unpause doesn't unpause. investigate that later
try {
if (this.stretcher.mode === Stretch.Basic) {
this.conf.arDetector.pause();
} else {
if (this.lastAr.type === AspectRatio.Automatic) {
this.conf.arDetector.unpause();
}
}
} catch (e) { // resizer starts before arDetector. this will do nothing but fail if arDetector isn't setup
}
// do stretch thingy
if (this.stretcher.mode === Stretch.NoStretch || this.stretcher.mode === Stretch.Conditional){
var stretchFactors = this.scaler.calculateCrop(ar);
if(! stretchFactors || stretchFactors.error){
this.logger.log('error', 'debug', `[Resizer::setAr] <rid:${this.resizerId}> failed to set AR due to problem with calculating crop. Error:`, (stretchFactors ? stretchFactors.error : stretchFactors));
if (stretchFactors.error === 'no_video'){
this.conf.destroy();
}
if (stretchFactors.error === 'illegal_video_dimensions') {
this.logger.log('error', 'debug', `[Resizer::setAr] <rid:${this.resizerId}> Illegal video dimensions found. We will pause everything.`)
}
return;
}
if(this.stretcher.mode === Stretch.Conditional){
this.stretcher.applyConditionalStretch(stretchFactors, ar.ratio);
}
this.logger.log('info', 'debug', "[Resizer::setAr] Processed stretch factors for ", this.stretcher.mode === Stretch.NoStretch ? 'stretch-free crop.' : 'crop with conditional stretch.', 'Stretch factors are:', stretchFactors);
} else if (this.stretcher.mode === Stretch.Hybrid) {
var stretchFactors = this.stretcher.calculateStretch(ar.ratio);
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for hybrid stretch/crop. Stretch factors are:', stretchFactors);
} else if (this.stretcher.mode === Stretch.Basic) {
var stretchFactors = this.stretcher.calculateBasicStretch();
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for basic stretch. Stretch factors are:', stretchFactors);
} else {
var stretchFactors = {xFactor: 1, yFactor: 1}
this.logger.log('error', 'debug', '[Resizer::setAr] Okay wtf happened? If you see this, something has gone wrong', stretchFactors,"\n------[ i n f o d u m p ]------\nstretcher:", this.stretcher);
}
this.zoom.applyZoom(stretchFactors);
//TODO: correct these two
var translate = this.computeOffsets(stretchFactors);
this.applyCss(stretchFactors, translate);
}
resetLastAr() {
this.lastAr = {type: AspectRatio.Initial};
}
setLastAr(override){
this.lastAr = override;
}
getLastAr(){
return this.lastAr;
}
setStretchMode(stretchMode){
this.stretcher.setStretchMode(stretchMode);
this.restore();
}
panHandler(event, forcePan) {
if (this.canPan || forcePan) {
if(!this.conf.player || !this.conf.player.element) {
return;
}
// dont allow weird floats
this.videoAlignment = VideoAlignment.Center;
const player = this.conf.player.element;
const relativeX = (event.pageX - player.offsetLeft) / player.offsetWidth;
const relativeY = (event.pageY - player.offsetTop) / player.offsetHeight;
this.logger.log('info', 'mousemove', "[Resizer::panHandler] mousemove.pageX, pageY:", event.pageX, event.pageY, "\nrelativeX/Y:", relativeX, relativeY)
this.setPan(relativeX, relativeY);
}
}
setPan(relativeMousePosX, relativeMousePosY){
// relativeMousePos[X|Y] - on scale from 0 to 1, how close is the mouse to player edges.
// use these values: top, left: 0, bottom, right: 1
if(! this.pan){
this.pan = {};
}
if (this.settings.active.miscSettings.mousePanReverseMouse) {
this.pan.relativeOffsetX = (relativeMousePosX * 1.1) - 0.55;
this.pan.relativeOffsetY = (relativeMousePosY * 1.1) - 0.55;
} else {
this.pan.relativeOffsetX = -(relativeMousePosX * 1.1) + 0.55;
this.pan.relativeOffsetY = -(relativeMousePosY * 1.1) + 0.55;
}
this.restore();
}
setVideoAlignment(videoAlignment) {
this.videoAlignment = videoAlignment;
this.restore();
}
restore() {
this.logger.log('info', 'debug', "[Resizer::restore] <rid:"+this.resizerId+"> attempting to restore aspect ratio. this & settings:", {'a_lastAr': this.lastAr, 'this': this, "settings": this.settings} );
// this is true until we verify that css has actually been applied
if(this.lastAr.type === AspectRatio.Initial){
this.setAr({type: AspectRatio.Reset});
}
else {
if (this.lastAr && this.lastAr.ratio === null) {
// if this is the case, we do nothing as we have the correct aspect ratio
// throw "Last ar is null!"
return;
}
this.setAr(this.lastAr, this.lastAr)
}
}
reset(){
this.setStretchMode(this.settings.active.sites[window.location.hostname] ? this.settings.active.sites[window.location.hostname].stretch : this.settings.active.sites['@global'].stretch);
this.zoom.setZoom(1);
this.resetPan();
this.setAr({type: AspectRatio.Reset});
}
setPanMode(mode) {
if (mode === 'enable') {
this.canPan = true;
} else if (mode === 'disable') {
this.canPan = false;
} else if (mode === 'toggle') {
this.canPan = !this.canPan;
}
}
resetPan(){
this.pan = undefined;
}
setZoom(zoomLevel, no_announce) {
this.zoom.setZoom(zoomLevel, no_announce);
}
zoomStep(step){
this.zoom.zoomStep(step);
}
resetZoom(){
this.zoom.setZoom(1);
this.restore();
}
resetCrop(){
this.setAr({type: AspectRatio.Reset});
}
resetStretch(){
this.stretcher.setStretchMode(Stretch.NoStretch);
this.restore();
}
// mostly internal stuff
computeOffsets(stretchFactors){
this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.settings.active.sites['@global'].videoAlignment);
const wdiff = this.conf.player.dimensions.width - this.conf.video.offsetWidth;
const hdiff = this.conf.player.dimensions.height - this.conf.video.offsetHeight;
const wdiffAfterZoom = this.conf.video.offsetWidth * stretchFactors.xFactor - this.conf.player.dimensions.width;
const hdiffAfterZoom = this.conf.video.offsetHeight * stretchFactors.yFactor - this.conf.player.dimensions.height;
var translate = {
x: wdiff * 0.5,
y: hdiff * 0.5,
};
if (this.pan) {
// don't offset when video is smaller than player
if(wdiffAfterZoom < 0 && hdiffAfterZoom < 0) {
return translate;
}
translate.x += wdiffAfterZoom * this.pan.relativeOffsetX * this.zoom.scale;
translate.y += hdiffAfterZoom * this.pan.relativeOffsetY * this.zoom.scale;
} else {
if (this.videoAlignment == VideoAlignment.Left) {
translate.x += wdiffAfterZoom * 0.5;
}
else if (this.videoAlignment == VideoAlignment.Right) {
translate.x -= wdiffAfterZoom * 0.5;
}
}
this.logger.log('info', 'debug', "[Resizer::_res_computeOffsets] <rid:"+this.resizerId+"> calculated offsets:\n\n",
'---- data in ----\n',
'player dimensions:', {w: this.conf.player.dimensions.width, h: this.conf.player.dimensions.height},
'video dimensions: ', {w: this.conf.video.offsetWidth, h: this.conf.video.offsetHeight},
'stretch factors: ', stretchFactors,
'pan & zoom: ', this.pan, this.zoom,
'\n\n---- data out ----\n',
'translate:', translate);
return translate;
}
buildStyleArray(existingStyleString, extraStyleString) {
if (existingStyleString) {
const styleArray = existingStyleString.split(";");
if (extraStyleString) {
const extraCss = extraStyleString.split(';');
let dup = false;
for (const ecss of extraCss) {
for (let i in styleArray) {
if (ecss.split(':')[0].trim() === styleArray[i].split(':')[0].trim()) {
dup = true;
styleArray[i] = ecss;
}
if (dup) {
dup = false;
continue;
}
styleArray.push(ecss);
}
}
}
for (var i in styleArray) {
styleArray[i] = styleArray[i].trim();
// some sites do 'top: 50%; left: 50%; transform: <transform>' to center videos.
// we dont wanna, because we already center videos on our own
if (styleArray[i].startsWith("transform:") ||
styleArray[i].startsWith("top:") ||
styleArray[i].startsWith("left:") ||
styleArray[i].startsWith("right:") ||
styleArray[i].startsWith("bottom:") ){
delete styleArray[i];
}
}
return styleArray;
}
return [];
}
buildStyleString(styleArray) {
let styleString = '';
for(var i in styleArray) {
if(styleArray[i]) {
styleString += styleArray[i] + " !important; ";
}
}
return styleString;
}
applyCss(stretchFactors, translate){
// apply extra CSS here. In case of duplicated properties, extraCss overrides
// default styleString
if (! this.video) {
this.logger.log('warn', 'debug', "[Resizer::applyCss] <rid:"+this.resizerId+"> Video went missing, doing nothing.");
this.conf.destroy();
return;
}
this.logger.log('info', 'resizer', "[Resizer::applyCss] <rid:"+this.resizerId+"> will apply css.", {stretchFactors, translate});
// save stuff for quick tests (before we turn numbers into css values):
this.currentVideoSettings = {
validFor: this.conf.player.dimensions,
// videoWidth: dimensions.width,
// videoHeight: dimensions.height
}
let extraStyleString;
try {
extraStyleString = this.settings.active.sites[window.location.host].DOM.video.additionalCss;
} catch (e) {
// do nothing. It's ok if no special settings are defined for this site, we'll just do defaults
}
const styleArray = this.buildStyleArray('', extraStyleString)
// add remaining elements
if (stretchFactors) {
styleArray.push(`transform: translate(${translate.x}px, ${translate.y}px) scale(${stretchFactors.xFactor}, ${stretchFactors.yFactor})`);
// important — guarantees video will be properly aligned
styleArray.push("top: 0px !important; left: 0px !important; bottom: 0px !important; right: 0px;");
// important — some websites (cough reddit redesign cough) may impose some dumb max-width and max-height
// restrictions. If site has dumb shit like 'max-width: 100%' and 'max-height: 100vh' in their CSS, that
// shit will prevent us from applying desired crop. This means we need to tell websites to fuck off with
// that crap. We know better.
styleArray.push("max-width: none !important; max-height: none !important;");
}
const styleString = `${this.buildStyleString(styleArray)}${extraStyleString || ''}`; // string returned by buildStyleString() should end with ; anyway
// build style string back
this.setStyleString(styleString);
}
setStyleString (styleString) {
this.currentCssValidFor = this.conf.player.dimensions;
const newCssString = this.prepareCss(styleString);
// inject new CSS or replace existing one
if (!this.userCss) {
this.injectCss(newCssString);
this.userCss = newCssString;
} else if (newCssString !== this.userCss) {
// we only replace css if it
this.replaceCss(this.userCss, newCssString);
this.userCss = newCssString;
}
}
}
export default Resizer;

Some files were not shown because too many files have changed in this diff Show More