aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.clang-format6
-rw-r--r--index.html106
-rw-r--r--main.js68
-rw-r--r--style.css141
4 files changed, 190 insertions, 131 deletions
diff --git a/.clang-format b/.clang-format
index bda6336..3130d2e 100644
--- a/.clang-format
+++ b/.clang-format
@@ -23,7 +23,7 @@ BraceWrapping:
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
- BeforeElse: false
+ BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
@@ -31,7 +31,7 @@ BraceWrapping:
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
-ColumnLimit: 80
+ColumnLimit: 100
ContinuationIndentWidth: 8
DeriveLineEnding: true
IndentCaseBlocks: false
@@ -61,4 +61,4 @@ UseTab: Always
# Taken from git's rules (Except PenaltyBreakAssignment)
PenaltyBreakAssignment: 100
-PenaltyExcessCharacter: 100 \ No newline at end of file
+PenaltyExcessCharacter: 100
diff --git a/index.html b/index.html
index 23e4a99..b5cc2a0 100644
--- a/index.html
+++ b/index.html
@@ -1,74 +1,60 @@
<!DOCTYPE html>
-<html>
+<html theme="light">
<head>
+ <title>YouTube Frame Timer</title>
<meta charset="utf-8">
<meta name="keywords" content="Speedrun, Retime">
<meta name="description" content="A speedrun retime tool">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>YouTube Frame Timer</title>
<link rel="stylesheet" href="style.css" type="text/css" media="screen">
- <!--
- <link rel="stylesheet" href="dark.css" type="text/css" media="screen">
- -->
- <script src="main.js"></script>
</head>
-<body class="stackedit">
- <div class="stackedit__html">
- <!--
- TODO: Finish dark theme!
+<body>
+ <label class="switch">
+ <input type="checkbox" id="page_theme" onclick="change_theme()">
+ <span class="slider" id="slider"></span>
+ </label>
+ <!-- This needs to come after the above switch -->
+ <script src="main.js"></script>
- <label class="switch">
- <input type="checkbox" id="color_preference" onclick="change_preferance()">
- <span class="slider round" id="slider"></span>
- </label>
- -->
- <h1>Speedrun Retimer without Ads</h1>
- <a href="https://github.com/Mango0x45/no-ad-retimer">
- Source code (GitHub)
- </a>
- <h3 id="video-framerate">Video Framerate</h3>
- <p>
- Right click the YouTube video and select “Stats for
- nerds.” The third line is “Current / Optimal Res” -
- find the two numbers after the @ and enter them for
- framerate.
- </p>
- <p>
- <label for="framerate">Framerate: </label>
- <input type="text" id="framerate" size="3" value="30"
- onchange='check_fps(event)'>
- </p>
- <h3>Video Endpoints</h3>
- <p>
- Find the starting point (you can use the <code>,</code>
- and <code>.</code> keys to find the exact frame). Right
- click the video and select “Copy debug info” and paste
- it for starting frame. Repeat for ending frame.
- </p>
- <p>
- <label for="startobj">Starting frame: </label>
- <input type="text" id="startobj" style='width:100%'
- onchange='parse_time(event)'>
- </p>
- <p>
- <label for="endobj">Ending frame: </label>
- <input type="text" id="endobj" style='width:100%'
- onchange='parse_time(event)'>
- </p>
- <h3 id="video-time">Video Time</h3>
- <input type="text" id="time" readonly size="40">
- <p>
- <textarea id="mod_message" cols="40" rows="3" readonly
- disabled>
- </textarea>
- </p>
- <button id="mod_message_button" disabled
- onclick="copy_mod_message()">
- Copy Mod Message to Clipboard
- </button>
- </div>
+ <h1>Speedrun Retimer without Ads</h1>
+ <a href="https://github.com/Mango0x45/no-ad-retimer" target="_blank">
+ Source code (GitHub)
+ </a>
+ <h3 id="video-framerate">Video Framerate</h3>
+ <p>
+ Right click the YouTube video and select “Stats for nerds.” The third line is
+ “Current / Optimal Res” - find the two numbers after the @ and enter them for
+ framerate.
+ </p>
+ <p>
+ <label for="framerate">Framerate: </label>
+ <input type="text" id="framerate" size="3" value="30" onchange='check_fps(event)'>
+ </p>
+ <h3>Video Endpoints</h3>
+ <p>
+ Find the starting point (you can use the <code>,</code> and <code>.</code> keys to
+ find the exact frame). Right click the video and select “Copy debug info” and paste
+ it for starting frame. Repeat for ending frame.
+ </p>
+ <p>
+ <label for="startobj">Starting frame: </label>
+ <input type="text" id="startobj" style='width:100%' onchange='parse_time(event)'>
+ </p>
+ <p>
+ <label for="endobj">Ending frame: </label>
+ <input type="text" id="endobj" style='width:100%' onchange='parse_time(event)'>
+ </p>
+ <h3 id="video-time">Video Time</h3>
+ <input type="text" id="time" readonly size="40">
+ <p>
+ <textarea id="mod_message" cols="40" rows="3" readonly disabled>
+ </textarea>
+ </p>
+ <button id="mod_message_button" disabled onclick="copy_mod_message()">
+ Copy Mod Message to Clipboard
+ </button>
</body>
</html>
diff --git a/main.js b/main.js
index ff338a8..8f04723 100644
--- a/main.js
+++ b/main.js
@@ -5,18 +5,25 @@ function compute()
const st = document.getElementById("startobj").value;
const et = document.getElementById("endobj").value;
+ /* Return early if not all fields are filled */
if (st === undefined || et === undefined || fps === undefined)
return;
+ /* Return early on a negative time */
const frames = (et - st) * fps;
+ if (frames < 0) {
+ document.getElementById("time").value = "Error: Negative time";
+ return;
+ }
+
const s = Math.round(frames / fps * 1000) / 1000;
/* Show the time and mod message in the DOM. */
const sf = Math.trunc(st * fps);
const ef = Math.trunc(et * fps);
const t = time_format(s);
- const mod_message = `Mod Note: Retimed (Start Frame: ${
- sf}, End Frame: ${ef}, FPS: ${fps}, Total Time: ${t})`;
+ const mod_message = `Mod Note: Retimed (Start Frame: ${sf}, End Frame: ${ef}, FPS: ${
+ fps}, Total Time: ${t})`;
document.getElementById("time").value = t;
document.getElementById("mod_message").disabled = false;
@@ -49,63 +56,62 @@ function copy_mod_message()
document.execCommand("copy");
}
-/*
- * If framerate is invalid, show an error message and disable start and end
- * frame fields.
- */
+/* If framerate is invalid, show an error message and disable start and end frame fields. */
function check_fps(event)
{
fps = event.target.value;
if (fps > 0 && fps % 1 == 0) {
document.getElementById("startobj").disabled = false;
document.getElementById("endobj").disabled = false;
- document.getElementById("compute_button").disabled = false;
- } else {
+ }
+ else {
document.getElementById("framerate")
.setCustomValidity("Please enter a valid framerate.");
document.getElementById("framerate").reportValidity();
document.getElementById("startobj").disabled = true;
document.getElementById("endobj").disabled = true;
- document.getElementById("compute_button").disabled = true;
}
}
/* Get current frame from input field (either start time or end time). */
function parse_time(event)
{
- let inptext_frame;
+ /* Return early if invalid JSON is passed (numbers are valid) */
+ let inp, dinfo;
try {
- inptext_frame = (JSON.parse(event.target.value)).cmt;
+ dinfo = JSON.parse(event.target.value);
} catch {
document.getElementById(event.target.id).value = "";
return;
}
- if (inptext_frame !== undefined) {
- const fps = parseInt(
- document.getElementById("framerate").value);
- const frame_from_obj = (t, fps) => Math.floor(t * fps) / fps;
- const fframe = frame_from_obj(inptext_frame, fps);
- document.getElementById(event.target.id).value = `${fframe}`;
+ /* If cmt isn't available fallback to lct, also allow raw numbers */
+ if (!(inp = dinfo.cmt) && !(inp = dinfo.lct) && typeof ((inp = dinfo)) !== "number") {
+ document.getElementById(event.target.id).value = "";
+ return;
}
- if (document.getElementById("startobj").value
- && document.getElementById("endobj").value)
+ /* Calculate the exact timestamp */
+ const fps = parseInt(document.getElementById("framerate").value);
+ const frame = Math.floor(inp * fps) / fps;
+ document.getElementById(event.target.id).value = `${frame}`;
+
+ /* If all fields are filled the compute */
+ if (document.getElementById("startobj").value && document.getElementById("endobj").value)
compute();
}
/* Change the users preferred theme. */
-function change_preferance()
+function change_theme()
{
- const color_switch = document.getElementById("color_preference");
-
- if (color_switch.checked) {
- document.body.classList.add("dark");
- document.body.classList.remove("light");
- localStorage.setItem("preference", "dark");
- } else {
- document.body.classList.add("light");
- document.body.classList.remove("dark");
- localStorage.setItem("preference", "light");
- }
+ const theme_switch = document.getElementById("page_theme");
+ const want = theme_switch.checked ? "dark" : "light";
+
+ document.documentElement.setAttribute("theme", want);
+ localStorage.setItem("theme", want);
}
+
+/* Automatically select the users preferred theme */
+const theme = localStorage.getItem("theme");
+document.documentElement.setAttribute("theme", theme);
+document.getElementById("page_theme").checked = (theme == "dark");
diff --git a/style.css b/style.css
index 8a9abb9..b16b4dd 100644
--- a/style.css
+++ b/style.css
@@ -1,10 +1,33 @@
-/*
- * This is a stripped down version of the style.css found over at
- * https://stackedit.io
- */
+:root {
+ --slider-color: #FFFFFF;
+ --slider-bg-on-color: #2196F3;
+ --slider-bg-off-color: #CCCCCC;
+}
+
+[theme="light"] {
+ --bg-color: #FFFFFF;
+ --text-color: #24292E;
+ --box-color: #DDDDDD;
+ --url-color: #0366D6;
+ --underline-color: #EAECEF;
+}
+
+[theme="dark"] {
+ --bg-color: #22272E;
+ --text-color: #ADBAC7;
+ --box-color: #2D333B;
+ --url-color: #539BF5;
+ --underline-color: #373E47;
+}
body {
- margin: 0;
+ background-color: var(--bg-color);
+ margin-bottom: 20px;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 70%;
+ padding-left: 30px;
+ padding-right: 30px;
}
html {
@@ -14,7 +37,6 @@ html {
}
body, html {
- color: rgba(0, 0, 0, 0.75);
font-size: 16px;
font-family: Lato, Helvetica Neue, Helvetica, sans-serif;
font-variant-ligatures: common-ligatures;
@@ -24,16 +46,12 @@ body, html {
}
h1 {
- font-size: 2em;
- margin: 0.67em 0;
-}
-
-h1:after {
+ border-bottom: 1px solid var(--underline-color);
content: "";
display: block;
+ font-size: 2em;
position: relative;
top: 0.33em;
- border-bottom: 1px solid hsla(0, 0%, 50%, 0.33);
}
h1, h3 {
@@ -45,9 +63,18 @@ p {
margin: 1.2em 0;
}
+
+button, h1, h3, input, p, textarea {
+ color: var(--text-color);
+}
+
+button, input, code, textarea {
+ background-color: var(--box-color);
+}
+
a {
background-color: transparent;
- color: #0c93e4;
+ color: var(--url-color);
text-decoration: underline;
text-decoration-skip: ink;
-webkit-text-decoration-skip: objects;
@@ -57,27 +84,20 @@ a:focus, a:hover {
text-decoration: none;
}
-code {
- background-color: rgba(0, 0, 0, 0.05);
- border-radius: 3px;
- padding: 2px 4px;
- font-family: Roboto Mono, Lucida Sans Typewriter, Lucida Console,
- monaco, Courrier, monospace;
- font-size: 0.85em;
- font-family: monospace, monospace;
- font-size: 1em;
-}
-
-code * {
- font-size: inherit;
-}
-
button {
overflow: visible;
text-transform: none;
}
+code {
+ font-family: Roboto Mono, Lucida Sans Typewriter, Lucida Console, monaco, Courrier,
+ monospace;
+ font-size: 1em;
+ padding: 2px 4px;
+}
+
input {
+ border: none;
overflow: visible;
}
@@ -85,18 +105,65 @@ textarea {
overflow: auto;
}
-button, input, textarea {
+button, code, input, textarea {
+ border: none;
+ border-radius: 6px;
font-family: sans-serif;
font-size: 100%;
- line-height: 1.15;
+ line-height: 1.5;
margin: 0;
}
-.stackedit__html {
- margin-bottom: 20px;
- margin-left: auto;
- margin-right: auto;
- padding-left: 30px;
- padding-right: 30px;
- max-width: 70%;
+.switch {
+ display: inline-flex;
+ float: right;
+ height: 34px;
+ position: relative;
+ width: 60px;
+}
+
+.switch input {
+ height: 0;
+ opacity: 0;
+ width: 0;
+}
+
+.slider {
+ background-color: var(--slider-bg-off-color);
+ border-radius: 34px;
+ bottom: 0;
+ position: absolute;
+ cursor: pointer;
+ left: 0;
+ right: 0;
+ top: 0;
+ transition: .4s;
+ -webkit-transition: .4s;
+}
+
+.slider:before {
+ background-color: var(--slider-color);
+ border-radius: 50%;
+ bottom: 4px;
+ content: "";
+ height: 26px;
+ left: 4px;
+ position: absolute;
+ transition: .4s;
+ width: 26px;
+ -webkit-transition: .4s;
+}
+
+input:checked + .slider {
+ background-color: var(--slider-bg-on-color);
+}
+
+input:focus + .slider {
+ box-shadow: 0 0 1px var(--slider-bg-on-color);
+}
+
+input:checked + .slider:before {
+ transform: translateX(26px);
+ -ms-transform: translateX(26px);
+ -webkit-transform: translateX(26px);
}