mirror of
https://github.com/weyne85/chaturbate-dvr.git
synced 2025-10-29 16:58:56 +00:00
Channel_file.go - fix issue with segments not correctly ending when they were supposed to Log_type.go - moved log type to it's own file, setup global logging (touches on issue #47) Main.go - added update_log_level handler, setting global log level Channel.go, channel_internal.go, channel_util.go - updated to use new log_type Manager.go - updated to use new log_type, update from .com to .global (issue #74) Channel_update.go, create_channel.go, delete_channel.go, get_channel.go, get_settings.go, listen_update.go, pause_channel.go, resume_channel.go, terminal_program.go - go fmt / go vet Chaturbate_channels.json.sample - added sample json of the channels file, for mapping in docker config List_channels.go - refactored to sort by online status, so online is always at the first ones you see Script.js - adjust default settings, added pagination, added global log logic Index.html - updated to use online version of tocas ui, added pagination, added global log logic, visual improvements Removal of local tocas folder since using online version
411 lines
19 KiB
HTML
411 lines
19 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/script.js"></script>
|
|
<script src="https://unpkg.com/alpinejs" defer></script>
|
|
<title>Chaturbate DVR</title>
|
|
</head>
|
|
<body x-data="data()">
|
|
<!-- Create Dialog -->
|
|
<dialog id="create-dialog" class="ts-modal is-large" data-clickaway="close">
|
|
<div class="content">
|
|
<!-- Header -->
|
|
<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 class="ts-close is-rounded is-large is-secondary" x-on:click="closeCreateDialog"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Header -->
|
|
|
|
<div class="ts-divider"></div>
|
|
|
|
<!-- Form -->
|
|
<div class="ts-content is-vertically-padded">
|
|
<!-- Field: 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">chaturbate.global/</div>
|
|
<input type="text" autofocus x-model="form_data.username" />
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced-small">Use commas to separate multiple channel names. For example, "channel1,channel2,channel3".</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Field: Channel Username -->
|
|
|
|
<!-- Field: Resolution -->
|
|
<div class="ts-control is-wide has-top-spaced-large">
|
|
<div class="label">Resolution</div>
|
|
<div class="content">
|
|
<div class="ts-grid">
|
|
<div class="column">
|
|
<div class="ts-select">
|
|
<select x-model="form_data.resolution">
|
|
<option value="2160">4K</option>
|
|
<option value="1440">2K</option>
|
|
<option value="1080">1080p</option>
|
|
<option value="720">720p</option>
|
|
<option value="540">540p</option>
|
|
<option value="480">480p</option>
|
|
<option value="240">240p</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="ts-select">
|
|
<select x-model="form_data.resolution_fallback">
|
|
<option value="up">or higher</option>
|
|
<option value="down">or lower</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced-small">
|
|
The <template x-if="form_data.resolution_fallback === 'up'"><span>higher</span></template
|
|
><template x-if="form_data.resolution_fallback === 'down'"><span>lower</span></template> resolution will be used if
|
|
<template x-if="form_data.resolution === '2160'"><span>4K</span></template
|
|
><template x-if="form_data.resolution === '1440'"><span>2K</span></template
|
|
><template x-if="form_data.resolution === '1080'"><span>1080p</span></template
|
|
><template x-if="form_data.resolution === '720'"><span>720p</span></template
|
|
><template x-if="form_data.resolution === '480'"><span>480p</span></template
|
|
><template x-if="form_data.resolution === '240'"><span>240p</span></template> was not available.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Field: Resolution -->
|
|
|
|
<!-- Field: Framerate -->
|
|
<div class="ts-control is-wide has-top-spaced-large">
|
|
<div class="label">Framerate</div>
|
|
<div class="content">
|
|
<div class="ts-wrap is-compact is-vertical has-top-spaced-small">
|
|
<label class="ts-radio">
|
|
<input name="framerate" value="60" type="radio" x-model="form_data.framerate" />
|
|
60 FPS
|
|
</label>
|
|
<label class="ts-radio">
|
|
<input name="framerate" value="30" type="radio" x-model="form_data.framerate" />
|
|
30 FPS
|
|
</label>
|
|
</div>
|
|
<template x-if="form_data.framerate === '60'">
|
|
<div class="ts-text is-description has-top-spaced-small">30 FPS will be used if 60 FPS was not available.</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- / Field: Framerate -->
|
|
|
|
<!-- Field: 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" x-model="form_data.filename_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>
|
|
<!-- / Field: Filename Pattern -->
|
|
|
|
<!-- Field: Check Interval -->
|
|
<input type="hidden" x-model="form_data.interval" />
|
|
<!-- / Field: Check Interval -->
|
|
|
|
<div class="ts-divider has-vertically-spaced-large"></div>
|
|
|
|
<!-- Field: Splitting Options -->
|
|
<div class="ts-control is-wide has-top-spaced">
|
|
<div class="label"></div>
|
|
<div class="content">
|
|
<details id="splitting-accordion" class="ts-accordion">
|
|
<summary>Splitting Options</summary>
|
|
<div class="ts-content is-rounded is-secondary has-top-spaced">
|
|
<div class="ts-grid is-2-columns">
|
|
<div class="column">
|
|
<div class="ts-text is-bold">by Filesize</div>
|
|
<div class="ts-input is-end-labeled has-top-spaced-small">
|
|
<input type="text" x-model="form_data.split_filesize" />
|
|
<span class="label">MB</span>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="ts-text is-bold">by Duration</div>
|
|
<div class="ts-input is-end-labeled has-top-spaced-small">
|
|
<input type="text" x-model="form_data.split_duration" />
|
|
<span class="label">Mins</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ts-text is-description has-top-spaced">Splitting will be disabled if both options are 0.</div>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
</div>
|
|
<!-- / Field: Splitting Options -->
|
|
</div>
|
|
<!-- / Form -->
|
|
|
|
<div class="ts-divider"></div>
|
|
|
|
<!-- Footer -->
|
|
<div class="ts-content is-secondary is-horizontally-padded">
|
|
<div class="ts-wrap is-end-aligned">
|
|
<button class="ts-button is-outlined is-secondary" x-on:click="closeCreateDialog">Cancel</button>
|
|
<button class="ts-button is-primary" x-on:click="submitCreateDialog">Add Channel</button>
|
|
</div>
|
|
</div>
|
|
<!-- / Footer -->
|
|
</div>
|
|
</dialog>
|
|
<!-- / Create Dialog -->
|
|
|
|
<!-- Main Section -->
|
|
<div class="ts-container has-vertically-padded-big">
|
|
<!-- 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 <span x-text="settings.version"></span></div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="ts-wrap">
|
|
<div class="ts-select">
|
|
<select x-model="settings.log_level">
|
|
<option>DEBUG</option>
|
|
<option>INFO</option>
|
|
<option>WARN</option>
|
|
<option>ERROR</option>
|
|
</select>
|
|
</div>
|
|
<button class="ts-button is-outlined is-negative is-start-icon" x-on:click="terminateProgram()">
|
|
<span class="ts-icon is-hand-icon"></span>
|
|
Terminate
|
|
</button>
|
|
<button class="ts-button is-start-icon" x-on:click="openCreateDialog">
|
|
<span class="ts-icon is-plus-icon"></span>
|
|
Add Channel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Header -->
|
|
|
|
<!-- Empty State -->
|
|
<template x-if="channels.length === 0">
|
|
<div>
|
|
<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 channel was recording</div>
|
|
<div class="description">Add a new Chaturbate channel to start the recording.</div>
|
|
<div class="action">
|
|
<button class="ts-button is-start-icon" x-on:click="openCreateDialog">
|
|
<span class="ts-icon is-plus-icon"></span>
|
|
Add Channel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<!-- / Empty State -->
|
|
|
|
<!-- Divider -->
|
|
<template x-if="channels.length > 0">
|
|
<div class="ts-divider is-start-text is-section">
|
|
<span class="ts-text is-description"
|
|
><span x-text="channels.length"></span>
|
|
<span x-show="channels.length < 2" style="display: none"> channel is</span>
|
|
<span x-show="channels.length > 1"> channels are</span> being recorded
|
|
</span>
|
|
<span class="ts-text is-description"> <span x-text="channels.filter(channel => channel.is_online).length"></span> online / <span x-text="channels.filter(channel => !channel.is_online).length"></span> offline </span>
|
|
</div>
|
|
</template>
|
|
<!-- / Divider -->
|
|
<!-- Pagination Controls -->
|
|
<div class="ts-pagination is-fluid" style="margin-bottom: 1rem;">
|
|
<a class="item" x-on:click="goToPage(1)" :class="{ 'is-disabled': currentPage === 1 }">«</a>
|
|
<template x-for="page in totalPages" :key="page">
|
|
<a class="item" x-on:click="goToPage(page)" :class="{ 'is-active': currentPage === page }" x-text="page"></a>
|
|
</template>
|
|
<a class="item" x-on:click="goToPage(totalPages)" :class="{ 'is-disabled': currentPage === totalPages }">»</a>
|
|
</div>
|
|
<!-- / Pagination controls -->
|
|
<div class="ts-wrap is-vertical is-relaxed">
|
|
<!-- Channel -->
|
|
<template x-for="channel in paginatedChannels" :key="channel.username">
|
|
<!-- <div class="ts-box is-horizontal"> -->
|
|
<div
|
|
class="ts-box is-horizontal"
|
|
:class="{
|
|
'is-positive is-top-indicated': channel.is_online && !channel.is_paused,
|
|
'is-negative is-top-indicated': !channel.is_online && !channel.is_paused,
|
|
'is-top-indicated': channel.is_paused
|
|
}"
|
|
>
|
|
<!-- Left Section -->
|
|
<div class="ts-content is-padded" style="flex: 1.25; display: flex; flex-direction: column">
|
|
<!-- Header -->
|
|
<div class="ts-grid is-middle-aligned">
|
|
<div class="column is-fluid">
|
|
<div class="ts-header">
|
|
<span x-text="channel.username"></span>
|
|
<template x-if="channel.is_online && !channel.is_paused">
|
|
<span class="ts-badge is-small is-start-spaced">RECORDING</span>
|
|
</template>
|
|
<template x-if="!channel.is_online && !channel.is_paused">
|
|
<span class="ts-badge is-secondary is-small is-start-spaced">OFFLINE</span>
|
|
</template>
|
|
<template x-if="channel.is_paused">
|
|
<span class="ts-badge is-negative is-small is-start-spaced">PAUSED</span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<button class="ts-button is-secondary is-short is-outlined is-dense" x-on:click="downloadLogs(channel.username)">Download Logs</button>
|
|
</div>
|
|
</div>
|
|
<!-- / Header -->
|
|
|
|
<!-- Logs -->
|
|
<div class="ts-input has-top-spaced" style="flex: 1">
|
|
<textarea class="has-full-height" x-bind:id="`${channel.username}-logs`" x-text="channel.logs.join('\n')" readonly></textarea>
|
|
</div>
|
|
<!-- / Logs -->
|
|
</div>
|
|
<!-- / Left Section -->
|
|
|
|
<div class="ts-divider is-vertical"></div>
|
|
|
|
<!-- Right Section -->
|
|
<div class="ts-content is-padded has-break-all" style="flex: 1; min-width: 300px">
|
|
<div class="ts-text is-description is-uppercased">Information</div>
|
|
|
|
<!-- Info: Channel URL -->
|
|
<div class="ts-grid has-top-spaced-large">
|
|
<div class="column has-leading-none" style="width: 16px">
|
|
<span class="ts-icon is-link-icon"></span>
|
|
</div>
|
|
<div class="column is-fluid has-leading-small">
|
|
<div class="ts-text is-label">Channel URL</div>
|
|
<a class="ts-text is-link is-external-link" x-bind:href="channel.channel_url" x-text="channel.channel_url" target="_blank"></a>
|
|
</div>
|
|
</div>
|
|
<!-- / Info: Channel URL -->
|
|
|
|
<!-- Info: Filename -->
|
|
<div class="ts-grid has-top-spaced">
|
|
<div class="column has-leading-none" style="width: 16px">
|
|
<span class="ts-icon is-folder-icon"></span>
|
|
</div>
|
|
<div class="column is-fluid has-leading-small">
|
|
<div class="ts-text is-label">Filename</div>
|
|
|
|
<template x-if="channel.filename">
|
|
<code class="ts-text is-code" x-text="channel.filename"></code>
|
|
</template>
|
|
<template x-if="!channel.filename">
|
|
<span>-</span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- / Info: Filename -->
|
|
|
|
<!-- Info: Last streamed at -->
|
|
<div class="ts-grid has-top-spaced">
|
|
<div class="column has-leading-none" style="width: 16px">
|
|
<span class="ts-icon is-tower-broadcast-icon"></span>
|
|
</div>
|
|
<div class="column is-fluid">
|
|
<div class="ts-text is-label">Last streamed at</div>
|
|
<div class="ts-text is-description">
|
|
<span x-text="channel.last_streamed_at"></span>
|
|
<template x-if="channel.is_online && !channel.is_paused">
|
|
<span>(NOW)</span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Info: Last streamed at -->
|
|
|
|
<!-- Info: Segment duration -->
|
|
<div class="ts-grid has-top-spaced">
|
|
<div class="column has-leading-none" style="width: 16px">
|
|
<span class="ts-icon is-clock-icon"></span>
|
|
</div>
|
|
<div class="column is-fluid">
|
|
<div class="ts-text is-label">Segment duration</div>
|
|
<div class="ts-text is-description">
|
|
<span x-text="channel.segment_duration"></span>
|
|
<template x-if="channel.split_duration !== '00:00:00'">
|
|
<span> / <span x-text="channel.split_duration"></span></span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Info: Segment duration -->
|
|
|
|
<!-- Info: Segment filesize -->
|
|
<div class="ts-grid has-top-spaced">
|
|
<div class="column has-leading-none" style="width: 16px">
|
|
<span class="ts-icon is-chart-pie-icon"></span>
|
|
</div>
|
|
<div class="column is-fluid">
|
|
<div class="ts-text is-label">Segment filesize</div>
|
|
<div class="ts-text is-description">
|
|
<span x-text="channel.segment_filesize"></span>
|
|
<template x-if="channel.split_filesize !== '0.00 MiB'">
|
|
<span> / <span x-text="channel.split_filesize"></span></span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- / Info: Segment filesize -->
|
|
|
|
<!-- Actions -->
|
|
<div class="ts-grid is-2-columns has-top-spaced-large">
|
|
<div class="column">
|
|
<template x-if="!channel.is_paused">
|
|
<button class="ts-button is-start-icon is-secondary is-fluid" x-bind:disabled="!channel.is_online" x-on:click="pauseChannel(channel.username)">
|
|
<span class="ts-icon is-pause-icon"></span>
|
|
Pause
|
|
</button>
|
|
</template>
|
|
<template x-if="channel.is_paused">
|
|
<button class="ts-button is-start-icon is-fluid" x-on:click="resumeChannel(channel.username)">
|
|
<span class="ts-icon is-play-icon"></span>
|
|
Resume
|
|
</button>
|
|
</template>
|
|
</div>
|
|
<div class="column">
|
|
<button class="ts-button is-start-icon is-secondary is-negative is-fluid" data-tooltip="Stop and remove the channel from the list." x-on:click="deleteChannel(channel.username)">
|
|
<span class="ts-icon is-stop-icon"></span>
|
|
Stop
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- / Actions -->
|
|
</div>
|
|
<!-- / Right Section -->
|
|
</div>
|
|
</template>
|
|
<!-- / Channel -->
|
|
</div>
|
|
</div>
|
|
<!-- / Main Section -->
|
|
</body>
|
|
</html>
|