mirror of
https://github.com/weyne85/chaturbate-dvr.git
synced 2025-10-29 16:58:56 +00:00
304 lines
17 KiB
HTML
304 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" class="is-secondary">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocas/5.0.1/tocas.min.css" />
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocas/5.0.1/tocas.min.js"></script>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;700&display=swap" rel="stylesheet" />
|
|
<script src="/static/scripts/htmx.min.js" crossorigin="anonymous"></script>
|
|
<script src="/static/scripts/sse.min.js" crossorigin="anonymous"></script>
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
|
|
<link rel="manifest" href="/static/site.webmanifest">
|
|
<title>Chaturbate DVR</title>
|
|
</head>
|
|
|
|
<body hx-ext="sse">
|
|
<!-- Main Section -->
|
|
<div class="ts-container has-vertically-padded-big" style="--width: 990px">
|
|
<!-- Header -->
|
|
<div class="ts-grid is-bottom-aligned">
|
|
<div class="column is-fluid">
|
|
<div class="ts-header is-huge is-uppercased is-heavy has-leading-small">Chaturbate DVR</div>
|
|
<div class="ts-text is-description is-bold">VERSION {{ .Config.Version }}</div>
|
|
</div>
|
|
<div class="column">
|
|
<button class="ts-button is-start-icon is-outlined" data-dialog="settings-dialog">
|
|
<span class="ts-icon is-gear-icon"></span>
|
|
Settings
|
|
</button>
|
|
</div>
|
|
<div class="column">
|
|
<button class="ts-button is-start-icon" data-dialog="create-dialog">
|
|
<span class="ts-icon is-plus-icon"></span>
|
|
Add Channel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- / Header -->
|
|
|
|
{{ if not .Channels }}
|
|
<!-- Blankslate -->
|
|
<div class="ts-divider has-vertically-spaced-large"></div>
|
|
<div class="ts-blankslate">
|
|
<span class="ts-icon is-eye-low-vision-icon"></span>
|
|
<div class="header">No channels are currently recording</div>
|
|
<div class="description">Add a new Chaturbate channel to start recording.</div>
|
|
<div class="action">
|
|
<button class="ts-button is-start-icon" data-dialog="create-dialog">
|
|
<span class="ts-icon is-plus-icon"></span>
|
|
Add Channel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- / Blankslate -->
|
|
{{ else }}
|
|
|
|
<!-- Channels -->
|
|
<div class="ts-wrap is-vertical has-top-spaced-large" sse-connect="/updates?stream=updates">
|
|
{{ range .Channels }}
|
|
<div class="ts-box is-horizontal">
|
|
<!-- Info Section -->
|
|
<div sse-swap="{{ .Username }}-info" class="ts-content is-padded has-break-all" style="width: 400px; line-height: 1.45; padding-right: 0">
|
|
{{ template "channel_info" . }}
|
|
</div>
|
|
<!-- / Info Section -->
|
|
|
|
<!-- Log Section -->
|
|
<div class="ts-content is-padded" style="flex: 1; gap: 0.8rem; display: flex; flex-direction: column">
|
|
<div class="ts-input" style="flex: 1">
|
|
<textarea class="has-full-height" readonly sse-swap="{{ .Username }}-log" style="scrollbar-width: thin">{{ range .Logs }}{{ . }}
{{ end }}</textarea>
|
|
</div>
|
|
<div>
|
|
<label class="ts-switch is-small" style="display: flex">
|
|
<input type="checkbox" checked />
|
|
Auto-Update & Scroll Logs
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<!-- / Log Section -->
|
|
</div>
|
|
{{ end }}
|
|
</div>
|
|
<!-- / Channels -->
|
|
{{ end }}
|
|
</div>
|
|
<!-- / Main Section -->
|
|
|
|
<!-- Settings Dialog -->
|
|
<dialog id="settings-dialog" class="ts-modal" style="--width: 680px">
|
|
<div class="content">
|
|
<form action="/update_config" method="POST">
|
|
<div class="ts-content is-horizontally-padded is-secondary">
|
|
<div class="ts-grid">
|
|
<div class="column is-fluid">
|
|
<div class="ts-header">Settings</div>
|
|
</div>
|
|
<div class="column">
|
|
<button type="reset" class="ts-close is-rounded is-large is-secondary" data-dialog="settings-dialog"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ts-divider"></div>
|
|
|
|
<div class="ts-content is-vertically-padded">
|
|
<!-- Cookies -->
|
|
<div class="ts-control is-wide">
|
|
<div class="label">Cookies</div>
|
|
<div class="content">
|
|
<div class="ts-input">
|
|
<textarea name="cookies" rows="5">{{ .Config.Cookies }}</textarea>
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced-small">Use semicolons to separate multiple cookies, e.g., "key1=value1; key2=value2". See <a class="ts-text is-link" href="https://github.com/teacat/chaturbate-dvr/?tab=readme-ov-file#-cookies--user-agent" target="_blank">README</a> for details.</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Cookies -->
|
|
|
|
<!-- User Agent -->
|
|
<div class="ts-control is-wide has-top-spaced-large">
|
|
<div class="label">User Agent</div>
|
|
<div class="content">
|
|
<div class="ts-input">
|
|
<textarea name="user_agent" rows="5">{{ .Config.UserAgent }}</textarea>
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced-small">User-Agent can be found using tools like <a class="ts-text is-link" href="https://www.whatismybrowser.com/detect/what-is-my-user-agent/" target="_blank">WhatIsMyBrowser</a>.</div>
|
|
</div>
|
|
</div>
|
|
<!-- / User Agent -->
|
|
</div>
|
|
|
|
<div class="ts-divider"></div>
|
|
|
|
<div class="ts-content is-secondary is-horizontally-padded">
|
|
<div class="ts-grid is-middle-aligned">
|
|
<div class="column is-fluid">
|
|
<div class="ts-text is-description">
|
|
<span class="ts-icon is-triangle-exclamation-icon is-end-spaced"></span>
|
|
Changes will be reverted after the program restarts
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<button type="reset" class="ts-button is-outlined is-secondary" data-dialog="settings-dialog">Cancel</button>
|
|
</div>
|
|
<div class="column">
|
|
<button type="submit" class="ts-button is-primary">Apply</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</dialog>
|
|
<!-- / Settings Dialog -->
|
|
|
|
<!-- Create Dialog -->
|
|
<dialog id="create-dialog" class="ts-modal" style="--width: 680px">
|
|
<div class="content">
|
|
<form action="/create_channel" method="POST">
|
|
<div class="ts-content is-horizontally-padded is-secondary">
|
|
<div class="ts-grid">
|
|
<div class="column is-fluid">
|
|
<div class="ts-header">Add Channel</div>
|
|
</div>
|
|
<div class="column">
|
|
<button type="reset" class="ts-close is-rounded is-large is-secondary" data-dialog="create-dialog"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ts-divider"></div>
|
|
|
|
<div class="ts-content is-vertically-padded">
|
|
<!-- Channel Username -->
|
|
<div class="ts-control is-wide">
|
|
<div class="label">Channel Username</div>
|
|
<div class="content">
|
|
<div class="ts-input is-start-labeled">
|
|
<div class="label">{{ .Config.Domain }}</div>
|
|
<input type="text" name="username" autofocus required />
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced-small">Use commas to separate multiple channel names, e.g. "channel1, channel2, channel3".</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Channel Username -->
|
|
|
|
<!-- Resolution -->
|
|
<div class="ts-control is-wide has-top-spaced-large">
|
|
<div class="label">Resolution</div>
|
|
<div class="content">
|
|
<div class="ts-select">
|
|
<select name="resolution">
|
|
<option value="2160" {{ if eq .Config.Resolution 2160 }}selected{{ end }}>4K</option>
|
|
<option value="1440" {{ if eq .Config.Resolution 1440 }}selected{{ end }}>2K</option>
|
|
<option value="1080" {{ if eq .Config.Resolution 1080 }}selected{{ end }}>1080p</option>
|
|
<option value="720" {{ if eq .Config.Resolution 720 }}selected{{ end }}>720p</option>
|
|
<option value="540" {{ if eq .Config.Resolution 540 }}selected{{ end }}>540p</option>
|
|
<option value="480" {{ if eq .Config.Resolution 480 }}selected{{ end }}>480p</option>
|
|
<option value="240" {{ if eq .Config.Resolution 240 }}selected{{ end }}>240p</option>
|
|
</select>
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced-small">The lower resolution will be used if the selected resolution is not available.</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Resolution -->
|
|
|
|
<!-- Framerate -->
|
|
<div class="ts-control is-wide has-top-spaced-large">
|
|
<div class="label">Framerate</div>
|
|
<div class="content is-padded">
|
|
<div class="ts-wrap is-compact is-vertical">
|
|
<label class="ts-radio">
|
|
<input type="radio" name="framerate" value="60" {{ if eq .Config.Framerate 60 }}checked{{ end }} />
|
|
60 FPS (or lower)
|
|
</label>
|
|
<label class="ts-radio">
|
|
<input type="radio" name="framerate" value="30" {{ if eq .Config.Framerate 30 }}checked{{ end }} />
|
|
30 FPS
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Framerate -->
|
|
|
|
<!-- Filename Pattern -->
|
|
<div class="ts-control is-wide has-top-spaced-large">
|
|
<div class="label">Filename Pattern</div>
|
|
<div class="content">
|
|
<div class="ts-input">
|
|
<input type="text" name="pattern" value="{{ .Config.Pattern }}" />
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced-small">
|
|
See the <a class="ts-text is-external-link is-link" href="https://github.com/teacat/chaturbate-dvr" target="_blank">README</a> for details.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Filename Pattern -->
|
|
|
|
<div class="ts-divider has-vertically-spaced-large"></div>
|
|
|
|
<!-- Splitting Options -->
|
|
<div class="ts-control is-wide has-top-spaced">
|
|
<div class="label">Splitting Options</div>
|
|
<div class="content">
|
|
<div class="ts-content is-padded is-secondary">
|
|
<div class="ts-grid is-relaxed is-2-columns">
|
|
<div class="column">
|
|
<div class="ts-text is-bold">Max Filesize</div>
|
|
<div class="ts-input is-end-labeled has-top-spaced-small">
|
|
<input type="number" name="max_filesize" value="{{ .Config.MaxFilesize }}" />
|
|
<span class="label">MB</span>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="ts-text is-bold">Max Duration</div>
|
|
<div class="ts-input is-end-labeled has-top-spaced-small">
|
|
<input type="number" name="max_duration" value="{{ .Config.MaxDuration }}" />
|
|
<span class="label">Min(s)</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced">Splitting will be disabled if both options are 0.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Splitting Options -->
|
|
</div>
|
|
|
|
<div class="ts-divider"></div>
|
|
|
|
<div class="ts-content is-secondary is-horizontally-padded">
|
|
<div class="ts-wrap is-end-aligned">
|
|
<button type="reset" class="ts-button is-outlined is-secondary" data-dialog="create-dialog">Cancel</button>
|
|
<button type="submit" class="ts-button is-primary">Add Channel</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</dialog>
|
|
<!-- / Create Dialog -->
|
|
|
|
<script>
|
|
// before content was swapped by HTMX
|
|
document.body.addEventListener("htmx:sseBeforeMessage", function (e) {
|
|
// stop it if "auto-update" was unchecked
|
|
if (!e.detail.elt.closest(".ts-box").querySelector("[type=checkbox]").checked) {
|
|
e.preventDefault()
|
|
return
|
|
}
|
|
// else scroll the textarea to bottom with async trick
|
|
setTimeout(() => {
|
|
let textarea = e.detail.elt.closest(".ts-box").querySelector("textarea")
|
|
textarea.scrollTop = textarea.scrollHeight
|
|
}, 0)
|
|
})
|
|
document.body.querySelectorAll("textarea").forEach((textarea) => {
|
|
textarea.scrollTop = textarea.scrollHeight
|
|
})
|
|
</script>
|
|
</body>
|
|
</html>
|