mirror of
https://github.com/owncast/owncast.git
synced 2024-11-25 06:12:23 +03:00
Merge branch 'master' into gek/current-stream-duration
This commit is contained in:
commit
a3613612eb
12 changed files with 239 additions and 205 deletions
|
@ -3,8 +3,6 @@ package config
|
|||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gabek/owncast/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -13,6 +11,7 @@ import (
|
|||
|
||||
//Config contains a reference to the configuration
|
||||
var Config *config
|
||||
var _default config
|
||||
|
||||
type config struct {
|
||||
ChatDatabaseFilePath string `yaml:"chatDatabaseFile"`
|
||||
|
@ -116,6 +115,10 @@ func (c *config) load(filePath string) error {
|
|||
}
|
||||
|
||||
func (c *config) verifySettings() error {
|
||||
if c.VideoSettings.StreamingKey == "" {
|
||||
return errors.New("No stream key set. Please set one in your config file.")
|
||||
}
|
||||
|
||||
if c.S3.Enabled && c.IPFS.Enabled {
|
||||
return errors.New("s3 and IPFS support cannot be enabled at the same time; choose one")
|
||||
}
|
||||
|
@ -137,32 +140,12 @@ func (c *config) verifySettings() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *config) GetFFMpegPath() string {
|
||||
if c.FFMpegPath != "" {
|
||||
return c.FFMpegPath
|
||||
}
|
||||
|
||||
cmd := exec.Command("which", "ffmpeg")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Panicln("Unable to determine path to ffmpeg. Please specify it in the config file.")
|
||||
}
|
||||
|
||||
path := strings.TrimSpace(string(out))
|
||||
|
||||
// Memoize it for future access
|
||||
c.FFMpegPath = path
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (c *config) GetVideoSegmentSecondsLength() int {
|
||||
if c.VideoSettings.ChunkLengthInSeconds != 0 {
|
||||
return c.VideoSettings.ChunkLengthInSeconds
|
||||
}
|
||||
|
||||
// Default
|
||||
return 4
|
||||
return _default.GetVideoSegmentSecondsLength()
|
||||
}
|
||||
|
||||
func (c *config) GetPublicHLSSavePath() string {
|
||||
|
@ -170,7 +153,7 @@ func (c *config) GetPublicHLSSavePath() string {
|
|||
return c.PublicHLSPath
|
||||
}
|
||||
|
||||
return "webroot/hls"
|
||||
return _default.PublicHLSPath
|
||||
}
|
||||
|
||||
func (c *config) GetPrivateHLSSavePath() string {
|
||||
|
@ -178,7 +161,7 @@ func (c *config) GetPrivateHLSSavePath() string {
|
|||
return c.PrivateHLSPath
|
||||
}
|
||||
|
||||
return "hls"
|
||||
return _default.PrivateHLSPath
|
||||
}
|
||||
|
||||
func (c *config) GetPublicWebServerPort() int {
|
||||
|
@ -186,8 +169,7 @@ func (c *config) GetPublicWebServerPort() int {
|
|||
return c.WebServerPort
|
||||
}
|
||||
|
||||
// Default web server port
|
||||
return 8080
|
||||
return _default.WebServerPort
|
||||
}
|
||||
|
||||
func (c *config) GetMaxNumberOfReferencedSegmentsInPlaylist() int {
|
||||
|
@ -195,7 +177,7 @@ func (c *config) GetMaxNumberOfReferencedSegmentsInPlaylist() int {
|
|||
return c.Files.MaxNumberInPlaylist
|
||||
}
|
||||
|
||||
return 20
|
||||
return _default.GetMaxNumberOfReferencedSegmentsInPlaylist()
|
||||
}
|
||||
|
||||
func (c *config) GetOfflineContentPath() string {
|
||||
|
@ -204,12 +186,29 @@ func (c *config) GetOfflineContentPath() string {
|
|||
}
|
||||
|
||||
// This is relative to the webroot, not the project root.
|
||||
return "static/offline.m4v"
|
||||
return _default.VideoSettings.OfflineContent
|
||||
}
|
||||
|
||||
func (c *config) GetFFMpegPath() string {
|
||||
if c.FFMpegPath != "" {
|
||||
return c.FFMpegPath
|
||||
}
|
||||
|
||||
return _default.FFMpegPath
|
||||
}
|
||||
|
||||
func (c *config) GetVideoStreamQualities() []StreamQuality {
|
||||
if len(c.VideoSettings.StreamQualities) > 0 {
|
||||
return c.VideoSettings.StreamQualities
|
||||
}
|
||||
|
||||
return _default.VideoSettings.StreamQualities
|
||||
}
|
||||
|
||||
//Load tries to load the configuration file
|
||||
func Load(filePath string, versionInfo string) error {
|
||||
Config = new(config)
|
||||
_default = getDefaults()
|
||||
|
||||
if err := Config.load(filePath); err != nil {
|
||||
return err
|
||||
|
@ -220,8 +219,10 @@ func Load(filePath string, versionInfo string) error {
|
|||
// Defaults
|
||||
|
||||
// This is relative to the webroot, not the project root.
|
||||
// Has to be set here instead of pulled from a getter
|
||||
// since it's serialized to JSON.
|
||||
if Config.InstanceDetails.ExtraInfoFile == "" {
|
||||
Config.InstanceDetails.ExtraInfoFile = "/static/content.md"
|
||||
Config.InstanceDetails.ExtraInfoFile = _default.InstanceDetails.ExtraInfoFile
|
||||
}
|
||||
|
||||
return Config.verifySettings()
|
||||
|
|
40
config/defaults.go
Normal file
40
config/defaults.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getDefaults() config {
|
||||
defaults := config{}
|
||||
defaults.WebServerPort = 8080
|
||||
defaults.FFMpegPath = getDefaultFFMpegPath()
|
||||
defaults.VideoSettings.ChunkLengthInSeconds = 4
|
||||
defaults.Files.MaxNumberInPlaylist = 5
|
||||
defaults.PublicHLSPath = "webroot/hls"
|
||||
defaults.PrivateHLSPath = "hls"
|
||||
defaults.VideoSettings.OfflineContent = "static/offline.m4v"
|
||||
defaults.InstanceDetails.ExtraInfoFile = "/static/content.md"
|
||||
|
||||
defaultQuality := StreamQuality{
|
||||
IsAudioPassthrough: true,
|
||||
VideoBitrate: 1200,
|
||||
EncoderPreset: "veryfast",
|
||||
}
|
||||
defaults.VideoSettings.StreamQualities = []StreamQuality{defaultQuality}
|
||||
|
||||
return defaults
|
||||
}
|
||||
|
||||
func getDefaultFFMpegPath() string {
|
||||
cmd := exec.Command("which", "ffmpeg")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Panicln("Unable to determine path to ffmpeg. Please specify it in the config file.")
|
||||
}
|
||||
|
||||
path := strings.TrimSpace(string(out))
|
||||
|
||||
return path
|
||||
}
|
|
@ -13,6 +13,7 @@ func GetStatus(w http.ResponseWriter, r *http.Request) {
|
|||
middleware.EnableCors(&w)
|
||||
|
||||
status := core.GetStatus()
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
json.NewEncoder(w).Encode(status)
|
||||
}
|
||||
|
|
|
@ -19,15 +19,6 @@ func Setup(listener models.ChatListener) {
|
|||
pingCh := make(chan models.PingMessage)
|
||||
doneCh := make(chan bool)
|
||||
errCh := make(chan error)
|
||||
|
||||
// Demo messages only. Remove me eventually!!!
|
||||
messages = append(messages, models.ChatMessage{"", "Tom Nook", "I'll be there with Bells on! Ho ho!", "https://gamepedia.cursecdn.com/animalcrossingpocketcamp_gamepedia_en/thumb/4/4f/Timmy_Icon.png/120px-Timmy_Icon.png?version=87b38d7d6130411d113486c2db151385", "demo-message-1", "CHAT", true, time.Now()})
|
||||
messages = append(messages, models.ChatMessage{"", "Redd", "Fool me once, shame on you. Fool me twice, stop foolin' me.", "https://vignette.wikia.nocookie.net/animalcrossing/images/3/3d/Redd2.gif/revision/latest?cb=20100710004252", "demo-message-2", "CHAT", true, time.Now()})
|
||||
messages = append(messages, models.ChatMessage{"", "Kevin", "You just caught me before I was about to go work out weeweewee!", "https://vignette.wikia.nocookie.net/animalcrossing/images/2/20/NH-Kevin_poster.png/revision/latest/scale-to-width-down/100?cb=20200410185817", "demo-message-3", "CHAT", true, time.Now()})
|
||||
messages = append(messages, models.ChatMessage{"", "Isabelle", " Isabelle is the mayor's highly capable secretary. She can be forgetful sometimes, but you can always count on her for information about the town. She wears her hair up in a bun that makes her look like a shih tzu. Mostly because she is one! She also has a twin brother named Digby.", "https://dodo.ac/np/images/thumb/7/7b/IsabelleTrophyWiiU.png/200px-IsabelleTrophyWiiU.png", "demo-message-4", "CHAT", true, time.Now()})
|
||||
messages = append(messages, models.ChatMessage{"", "Judy", "myohmy, I'm dancing my dreams away.", "https://vignette.wikia.nocookie.net/animalcrossing/images/5/50/NH-Judy_poster.png/revision/latest/scale-to-width-down/100?cb=20200522063219", "demo-message-5", "CHAT", true, time.Now()})
|
||||
messages = append(messages, models.ChatMessage{"", "Blathers", "Blathers is an owl with brown feathers. His face is white and he has a yellow beak. His arms are wing shaped and he has yellow talons. His eyes are very big with small black irises. He also has big pink cheek circles on his cheeks. His belly appears to be checkered in diamonds with light brown and white squares, similar to an argyle vest, which is traditionally associated with academia. His green bowtie further alludes to his academic nature.", "https://vignette.wikia.nocookie.net/animalcrossing/images/b/b3/NH-character-Blathers.png/revision/latest?cb=20200229053519", "demo-message-6", "CHAT", true, time.Now()})
|
||||
|
||||
messages = append(messages, getChatHistory()...)
|
||||
|
||||
_server = &server{
|
||||
|
|
|
@ -111,6 +111,7 @@ func (c *Client) listenRead() {
|
|||
msg.ID = id
|
||||
msg.MessageType = "CHAT"
|
||||
msg.Timestamp = time.Now()
|
||||
msg.Visible = true
|
||||
|
||||
if err := websocket.JSON.Receive(c.ws, &msg); err == io.EOF {
|
||||
c.doneCh <- true
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"github.com/gabek/owncast/config"
|
||||
"github.com/gabek/owncast/models"
|
||||
)
|
||||
|
||||
|
@ -56,12 +57,6 @@ func (s *server) err(err error) {
|
|||
s.errCh <- err
|
||||
}
|
||||
|
||||
func (s *server) sendPastMessages(c *Client) {
|
||||
for _, msg := range s.Messages {
|
||||
c.Write(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) sendAll(msg models.ChatMessage) {
|
||||
for _, c := range s.Clients {
|
||||
c.Write(msg)
|
||||
|
@ -104,7 +99,7 @@ func (s *server) Listen() {
|
|||
s.Clients[c.id] = c
|
||||
|
||||
s.listener.ClientAdded(c.id)
|
||||
s.sendPastMessages(c)
|
||||
s.sendWelcomeMessageToClient(c)
|
||||
|
||||
// remove a client
|
||||
case c := <-s.delCh:
|
||||
|
@ -128,3 +123,14 @@ func (s *server) Listen() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) sendWelcomeMessageToClient(c *Client) {
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
initialChatMessageText := fmt.Sprintf("Welcome to %s! %s", config.Config.InstanceDetails.Title, config.Config.InstanceDetails.Summary)
|
||||
initialMessage := models.ChatMessage{"owncast-server", config.Config.InstanceDetails.Name, initialChatMessageText, config.Config.InstanceDetails.Logo["small"], "initial-message-1", "CHAT", true, time.Now()}
|
||||
c.Write(initialMessage)
|
||||
}()
|
||||
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ videoSettings:
|
|||
audioPassthrough: true
|
||||
# The slower the preset the higher quality the video is.
|
||||
# Select a preset from https://trac.ffmpeg.org/wiki/Encode/H.264
|
||||
encoderPreset: superfast
|
||||
# "superfast" and "ultrafast" are generally not recommended since they look bad.
|
||||
encoderPreset: veryfast
|
||||
|
||||
- medium:
|
||||
videoBitrate: 800
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<body class="bg-gray-300 text-gray-800">
|
||||
|
||||
<div id="app-container" class="flex no-chat">
|
||||
<div id="app-container" class="flex chat">
|
||||
<div id="top-content">
|
||||
<header class="flex border-b border-gray-900 border-solid shadow-md">
|
||||
<h1 v-cloak class="flex text-gray-400">
|
||||
|
@ -84,7 +84,7 @@
|
|||
</section>
|
||||
</main>
|
||||
|
||||
<section id="user-content" v-if="layout === 'desktop'" aria-label="User information">
|
||||
<section id="user-content" aria-label="User information">
|
||||
<user-details
|
||||
v-bind:logo="logo"
|
||||
v-bind:platforms="socialHandles"
|
||||
|
@ -96,13 +96,13 @@
|
|||
|
||||
</section>
|
||||
|
||||
<owncast-footer v-if="layout === 'desktop'" v-bind:app-version="appVersion"></owncast-footer>
|
||||
<owncast-footer v-bind:app-version="appVersion"></owncast-footer>
|
||||
|
||||
</div>
|
||||
|
||||
<section id="chat-container-wrap" class="flex">
|
||||
|
||||
<div v-if="layout !== 'desktop'" id="user-content-touch">
|
||||
<!-- <div v-if="layout !== 'desktop'" id="user-content-touch">
|
||||
<user-details
|
||||
v-bind:logo="logo"
|
||||
v-bind:platforms="socialHandles"
|
||||
|
@ -114,7 +114,7 @@
|
|||
|
||||
<owncast-footer v-bind:app-version="appVersion"></owncast-footer>
|
||||
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div id="chat-container" class="bg-gray-800">
|
||||
<div id="messages-container">
|
||||
|
@ -165,7 +165,6 @@
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="js/usercolors.js"></script>
|
||||
<script src="js/utils.js"></script>
|
||||
<script src="js/message.js"></script>
|
||||
|
@ -179,5 +178,32 @@
|
|||
app.init();
|
||||
})();
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<style>
|
||||
[v-cloak] { display: none; }
|
||||
.noscript {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.noscript a {
|
||||
display: inline;
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
<div class="noscript">
|
||||
<img src="https://github.com/gabek/owncast/raw/master/doc/logo.png">
|
||||
<br/>
|
||||
<p>
|
||||
This <a href="https://github.com/gabek/owncast" target="_blank">Owncast</a> stream requires Javascript to play.
|
||||
</p>
|
||||
</div>
|
||||
</noscript>
|
||||
</body>
|
||||
</html>
|
|
@ -46,7 +46,6 @@ class Owncast {
|
|||
el: '#app-container',
|
||||
data: {
|
||||
isOnline: false,
|
||||
layout: hasTouchScreen() ? 'touch' : 'desktop',
|
||||
messages: [],
|
||||
overallMaxViewerCount: 0,
|
||||
sessionMaxViewerCount: 0,
|
||||
|
@ -86,6 +85,8 @@ class Owncast {
|
|||
onError: this.handlePlayerError,
|
||||
});
|
||||
this.player.init();
|
||||
|
||||
this.getChatHistory();
|
||||
};
|
||||
|
||||
setConfigData(data) {
|
||||
|
@ -132,17 +133,21 @@ class Owncast {
|
|||
return;
|
||||
}
|
||||
const message = new Message(model);
|
||||
const existing = this.vueApp.messages.filter(function (item) {
|
||||
return item.id === message.id;
|
||||
})
|
||||
if (existing.length === 0 || !existing) {
|
||||
this.vueApp.messages = [...this.vueApp.messages, message];
|
||||
}
|
||||
this.addMessage(message);
|
||||
};
|
||||
this.websocket = ws;
|
||||
this.messagingInterface.setWebsocket(this.websocket);
|
||||
};
|
||||
|
||||
addMessage(message) {
|
||||
const existing = this.vueApp.messages.filter(function (item) {
|
||||
return item.id === message.id;
|
||||
})
|
||||
if (existing.length === 0 || !existing) {
|
||||
this.vueApp.messages = [...this.vueApp.messages, message];
|
||||
}
|
||||
}
|
||||
|
||||
// fetch /config data
|
||||
getConfig() {
|
||||
fetch(URL_CONFIG)
|
||||
|
@ -296,4 +301,18 @@ class Owncast {
|
|||
this.handleOfflineMode();
|
||||
// stop timers?
|
||||
};
|
||||
|
||||
async getChatHistory() {
|
||||
const url = "/chat";
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
const messages = data.map(function (message) {
|
||||
return new Message(message);
|
||||
})
|
||||
this.setChatHistory(messages);
|
||||
}
|
||||
|
||||
setChatHistory(messages) {
|
||||
this.vueApp.messages = messages.concat(this.vueApp.messages);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -74,13 +74,11 @@ class MessagingInterface {
|
|||
this.initLocalStates();
|
||||
|
||||
if (hasTouchScreen()) {
|
||||
this.scrollableMessagesContainer = document.body;
|
||||
setVHvar();
|
||||
window.addEventListener("orientationchange", setVHvar);
|
||||
this.tagAppContainer.classList.add('touch-screen');
|
||||
window.onorientationchange = this.handleOrientationChange.bind(this);
|
||||
this.handleOrientationChange();
|
||||
} else {
|
||||
this.tagAppContainer.classList.add('desktop');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setWebsocket(socket) {
|
||||
|
@ -93,7 +91,7 @@ class MessagingInterface {
|
|||
getLocalStorage(KEY_AVATAR) || generateAvatar(`${this.username}${Date.now()}`);
|
||||
this.updateUsernameFields(this.username);
|
||||
|
||||
this.chatDisplayed = getLocalStorage(KEY_CHAT_DISPLAYED) || false;
|
||||
this.chatDisplayed = getLocalStorage(KEY_CHAT_DISPLAYED) || true;
|
||||
this.displayChat();
|
||||
}
|
||||
|
||||
|
@ -112,22 +110,9 @@ class MessagingInterface {
|
|||
this.tagAppContainer.classList.add('no-chat');
|
||||
this.tagAppContainer.classList.remove('chat');
|
||||
}
|
||||
this.setChatPlaceholderText();
|
||||
}
|
||||
|
||||
handleOrientationChange() {
|
||||
var isPortrait = Math.abs(window.orientation % 180) === 0;
|
||||
if(!isPortrait) {
|
||||
if (document.body.clientWidth < 1024) {
|
||||
this.tagAppContainer.classList.add('no-chat');
|
||||
this.tagAppContainer.classList.add('landscape');
|
||||
}
|
||||
} else {
|
||||
if (this.chatDisplayed) {
|
||||
this.tagAppContainer.classList.remove('no-chat');
|
||||
}
|
||||
this.tagAppContainer.classList.remove('landscape');
|
||||
}
|
||||
}
|
||||
|
||||
handleChatToggle() {
|
||||
this.chatDisplayed = !this.chatDisplayed;
|
||||
|
@ -241,6 +226,12 @@ class MessagingInterface {
|
|||
// clear out things.
|
||||
this.formMessageInput.value = '';
|
||||
this.tagMessageFormWarning.innerText = '';
|
||||
|
||||
const hasSentFirstChatMessage = getLocalStorage(KEY_CHAT_FIRST_MESSAGE_SENT);
|
||||
if (!hasSentFirstChatMessage) {
|
||||
setLocalStorage(KEY_CHAT_FIRST_MESSAGE_SENT, true);
|
||||
this.setChatPlaceholderText();
|
||||
}
|
||||
}
|
||||
|
||||
disableChat() {
|
||||
|
@ -248,14 +239,22 @@ class MessagingInterface {
|
|||
this.formMessageInput.disabled = true;
|
||||
this.formMessageInput.placeholder = "Chat is offline."
|
||||
}
|
||||
// also show "disabled" text/message somewhere.
|
||||
}
|
||||
enableChat() {
|
||||
if (this.formMessageInput) {
|
||||
this.formMessageInput.disabled = false;
|
||||
this.formMessageInput.placeholder = "Message"
|
||||
this.setChatPlaceholderText();
|
||||
}
|
||||
}
|
||||
|
||||
setChatPlaceholderText() {
|
||||
const firstMessageChatPlacholderText = "Type here to chat, no account necessary.";
|
||||
const chatPlaceholderText = "Message"
|
||||
|
||||
const hasSentFirstChatMessage = getLocalStorage(KEY_CHAT_FIRST_MESSAGE_SENT);
|
||||
this.formMessageInput.placeholder = hasSentFirstChatMessage ? chatPlaceholderText : firstMessageChatPlacholderText
|
||||
}
|
||||
|
||||
// handle Vue.js message display
|
||||
onReceivedMessages(newMessages, oldMessages) {
|
||||
if (newMessages.length !== oldMessages.length) {
|
||||
|
|
|
@ -32,6 +32,9 @@ const VIDEO_SRC = {
|
|||
const VIDEO_OPTIONS = {
|
||||
autoplay: false,
|
||||
liveui: true, // try this
|
||||
liveTracker: {
|
||||
trackingThreshold: 0,
|
||||
},
|
||||
sources: [VIDEO_SRC],
|
||||
};
|
||||
|
||||
|
@ -39,6 +42,7 @@ const VIDEO_OPTIONS = {
|
|||
const KEY_USERNAME = 'owncast_username';
|
||||
const KEY_AVATAR = 'owncast_avatar';
|
||||
const KEY_CHAT_DISPLAYED = 'owncast_chat';
|
||||
const KEY_CHAT_FIRST_MESSAGE_SENT = 'owncast_first_message_sent';
|
||||
|
||||
const TIMER_STATUS_UPDATE = 5000; // ms
|
||||
const TIMER_WEBSOCKET_RECONNECT = 5000; // ms
|
||||
|
@ -154,4 +158,11 @@ function secondsToHMMSS(seconds = 0) {
|
|||
const secsString = secs < 10 ? `0${secs}` : `${secs}`;
|
||||
|
||||
return hoursString + minString + secsString;
|
||||
}
|
||||
}
|
||||
|
||||
function setVHvar() {
|
||||
var vh = window.innerHeight * 0.01;
|
||||
// Then we set the value in the --vh custom property to the root of the document
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
console.log("== new vh", vh)
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ body {
|
|||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
/* vuejs attribute to hide things before content ready */
|
||||
[v-cloak] { visibility: hidden; }
|
||||
|
||||
::-webkit-scrollbar {
|
||||
|
@ -323,9 +323,11 @@ h2 {
|
|||
#video {
|
||||
transition: opacity .5s;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.online #video {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
|
||||
|
@ -364,6 +366,9 @@ h2 {
|
|||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.touch-screen #chat-container {
|
||||
height: calc(100vh - var(--header-height) - 3vh);
|
||||
}
|
||||
|
||||
|
||||
#messages-container {
|
||||
|
@ -426,83 +431,6 @@ h2 {
|
|||
|
||||
/* ************************************************8 */
|
||||
|
||||
.landscape #chat-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
/* ************************************************8 */
|
||||
|
||||
.touch-screen header {
|
||||
position: relative;
|
||||
}
|
||||
.touch-screen #top-content {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
||||
.touch-screen .user-content {
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
}
|
||||
.touch-screen .user-image {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
|
||||
.touch-screen #stream-info {
|
||||
height: 2.5em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.touch-screen #chat-container-wrap {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
margin-top: calc(var(--header-height) + var(--video-container-height) + 2.5em);
|
||||
}
|
||||
.touch-screen #chat-container {
|
||||
height: auto;
|
||||
position: relative;
|
||||
right: unset;
|
||||
top: unset;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.touch-screen.chat #video-container,
|
||||
.touch-screen.chat #stream-info,
|
||||
.touch-screen.chat #user-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.touch-screen #video-container {
|
||||
margin-top: 0;
|
||||
}
|
||||
.touch-screen .owncast-video-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.touch-screen #user-content-touch {
|
||||
display: none;
|
||||
}
|
||||
.touch-screen #chat-container {
|
||||
display: block;
|
||||
}
|
||||
.touch-screen.no-chat #user-content-touch {
|
||||
display: block;
|
||||
}
|
||||
.touch-screen.no-chat #chat-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
|
||||
@media screen and (max-width: 860px) {
|
||||
:root {
|
||||
|
@ -516,33 +444,12 @@ h2 {
|
|||
|
||||
}
|
||||
|
||||
/* single col layout */
|
||||
@media screen and (max-width: 640px ) {
|
||||
:root {
|
||||
--video-container-height: 36vh;
|
||||
--right-col-width: 0;
|
||||
--video-container-height: 40vh;
|
||||
}
|
||||
|
||||
.desktop {
|
||||
--video-container-height: 50vh;
|
||||
}
|
||||
.desktop #chat-container {
|
||||
height: auto;
|
||||
position: relative;
|
||||
right: unset;
|
||||
top: unset;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.desktop.chat #video-container,
|
||||
.desktop.chat #stream-info,
|
||||
.desktop.chat #user-content {
|
||||
width: 100%;
|
||||
}
|
||||
.desktop #footer,
|
||||
.desktop.chat #user-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#logo-container {
|
||||
display: none;
|
||||
}
|
||||
|
@ -552,29 +459,60 @@ h2 {
|
|||
#user-options-container {
|
||||
max-width: 41%;
|
||||
}
|
||||
|
||||
#chat-container {
|
||||
width: 100%;
|
||||
position: static;
|
||||
/* min-height: calc(100vh - var(--header-height)); */
|
||||
height: calc(100vh - var(--header-height) - var(--video-container-height) - 3vh)
|
||||
}
|
||||
#messages-container {
|
||||
min-height: unset;
|
||||
}
|
||||
#user-content {
|
||||
width: 100%;
|
||||
}
|
||||
#stream-info {
|
||||
width: 100%;
|
||||
}
|
||||
#video-container {
|
||||
width: 100%;
|
||||
}
|
||||
.chat #video-container {
|
||||
width: 100%;
|
||||
}
|
||||
.chat #user-content {
|
||||
display: none;
|
||||
}
|
||||
.chat footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (orientation: landscape) and (min-width: 1024px) {
|
||||
/* try not making the video fixed position for now */
|
||||
@media (min-height: 861px) {
|
||||
/* main {
|
||||
position: fixed;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
}
|
||||
#user-content {
|
||||
margin-top: calc(var(--video-container-height) + var(--header-height) + 2em)
|
||||
} */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@media screen and (max-height: 860px ) {
|
||||
:root {
|
||||
--video-container-height: 65vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (orientation: landscape) and (max-width: 1024px) {
|
||||
:root .landscape {
|
||||
--video-container-height: 75vh;
|
||||
}
|
||||
.touch-screen.landscape #chat-container-wrap {
|
||||
margin-top: calc(var(--header-height) + var(--video-container-height));
|
||||
}
|
||||
.touch-screen.landscape .user-content {
|
||||
display: block;
|
||||
}
|
||||
.touch-screen.landscape #chat-container {
|
||||
display: none;
|
||||
}
|
||||
.touch-screen.landscape #chat-toggle {
|
||||
display: none;
|
||||
--video-container-height: 40vh;
|
||||
}
|
||||
.user-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue