mirror of
https://github.com/pischule/memevizor.git
synced 2025-12-19 06:56:42 +00:00
Implement videos
This commit is contained in:
@@ -32,7 +32,7 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
implementation("io.github.kotlin-telegram-bot.kotlin-telegram-bot:telegram:6.3.0")
|
implementation("io.github.kotlin-telegram-bot.kotlin-telegram-bot:telegram:6.3.0")
|
||||||
implementation("io.github.oshai:kotlin-logging-jvm:7.0.3")
|
implementation("io.github.oshai:kotlin-logging-jvm:7.0.3")
|
||||||
implementation(awssdk.services.s3)
|
implementation("io.minio:minio:8.5.17")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
|||||||
@@ -4,10 +4,4 @@ dependencyResolutionManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
versionCatalogs {
|
|
||||||
create("awssdk") {
|
|
||||||
from("aws.sdk.kotlin:version-catalog:1.4.56")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,14 +4,17 @@ import com.github.kotlintelegrambot.Bot
|
|||||||
import com.github.kotlintelegrambot.bot
|
import com.github.kotlintelegrambot.bot
|
||||||
import com.github.kotlintelegrambot.dispatch
|
import com.github.kotlintelegrambot.dispatch
|
||||||
import com.github.kotlintelegrambot.dispatcher.command
|
import com.github.kotlintelegrambot.dispatcher.command
|
||||||
|
import com.github.kotlintelegrambot.dispatcher.handlers.media.MediaHandlerEnvironment
|
||||||
import com.github.kotlintelegrambot.dispatcher.message
|
import com.github.kotlintelegrambot.dispatcher.message
|
||||||
import com.github.kotlintelegrambot.dispatcher.photos
|
import com.github.kotlintelegrambot.dispatcher.photos
|
||||||
|
import com.github.kotlintelegrambot.dispatcher.video
|
||||||
import com.github.kotlintelegrambot.entities.ChatId
|
import com.github.kotlintelegrambot.entities.ChatId
|
||||||
import com.github.kotlintelegrambot.entities.Message
|
import com.github.kotlintelegrambot.entities.Message
|
||||||
import com.github.kotlintelegrambot.entities.ParseMode
|
import com.github.kotlintelegrambot.entities.ParseMode
|
||||||
import com.pischule.memevizor.bot.handler.PhotoHandlerService
|
import com.pischule.memevizor.bot.handler.MediaHandlerService
|
||||||
import com.pischule.memevizor.bot.handler.ThisCommandHandlerService
|
import com.pischule.memevizor.bot.handler.ThisCommandHandlerService
|
||||||
import com.pischule.memevizor.util.getMaxResPhotoId
|
import com.pischule.memevizor.util.getMaxResPhotoId
|
||||||
|
import com.pischule.memevizor.util.getVideoFileId
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import io.github.oshai.kotlinlogging.withLoggingContext
|
import io.github.oshai.kotlinlogging.withLoggingContext
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
@@ -25,7 +28,7 @@ private val logger = KotlinLogging.logger {}
|
|||||||
class BotConfiguration(
|
class BotConfiguration(
|
||||||
private val botProps: BotProps,
|
private val botProps: BotProps,
|
||||||
private val thisCommandHandlerService: ThisCommandHandlerService,
|
private val thisCommandHandlerService: ThisCommandHandlerService,
|
||||||
private val photoHandlerService: PhotoHandlerService,
|
private val mediaHandlerService: MediaHandlerService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -46,15 +49,8 @@ class BotConfiguration(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
photos {
|
photos { handleMedia() }
|
||||||
withLoggingContext(messageContext(message)) {
|
video { handleMedia() }
|
||||||
try {
|
|
||||||
photoHandlerService.create(this)
|
|
||||||
} catch (e: Error) {
|
|
||||||
logger.error(e) { "Error while handling photo" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
command("whoami") {
|
command("whoami") {
|
||||||
withLoggingContext(messageContext(message)) {
|
withLoggingContext(messageContext(message)) {
|
||||||
bot.sendMessage(
|
bot.sendMessage(
|
||||||
@@ -67,12 +63,22 @@ class BotConfiguration(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun MediaHandlerEnvironment<*>.handleMedia() {
|
||||||
|
withLoggingContext(messageContext(message)) {
|
||||||
|
try {
|
||||||
|
mediaHandlerService.create(this)
|
||||||
|
} catch (e: Error) {
|
||||||
|
logger.error(e) { "Error while handling photo" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun messageContext(message: Message): Map<String, String?> =
|
private fun messageContext(message: Message): Map<String, String?> =
|
||||||
mapOf(
|
mapOf(
|
||||||
"message_id" to message.messageId.toString(),
|
"message_id" to message.messageId.toString(),
|
||||||
"chat_id" to message.chat.id.toString(),
|
"chat_id" to message.chat.id.toString(),
|
||||||
"from_user_id" to message.from?.id.toString(),
|
"from_user_id" to message.from?.id.toString(),
|
||||||
"from_user_username" to message.from?.username.toString(),
|
"from_user_username" to message.from?.username.toString(),
|
||||||
"file_id" to message.getMaxResPhotoId(),
|
"file_id" to (message.getMaxResPhotoId() ?: message.getVideoFileId()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.pischule.memevizor.bot.handler
|
|||||||
|
|
||||||
import com.github.kotlintelegrambot.dispatcher.handlers.media.MediaHandlerEnvironment
|
import com.github.kotlintelegrambot.dispatcher.handlers.media.MediaHandlerEnvironment
|
||||||
import com.github.kotlintelegrambot.entities.ChatId
|
import com.github.kotlintelegrambot.entities.ChatId
|
||||||
import com.github.kotlintelegrambot.entities.files.PhotoSize
|
|
||||||
import com.github.kotlintelegrambot.entities.reaction.ReactionType
|
import com.github.kotlintelegrambot.entities.reaction.ReactionType
|
||||||
import com.pischule.memevizor.bot.BotProps
|
import com.pischule.memevizor.bot.BotProps
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
@@ -11,21 +10,21 @@ import org.springframework.stereotype.Service
|
|||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class PhotoHandlerService(private val botProps: BotProps) {
|
class MediaHandlerService(private val botProps: BotProps) {
|
||||||
|
|
||||||
suspend fun create(env: MediaHandlerEnvironment<List<PhotoSize>>) {
|
suspend fun create(env: MediaHandlerEnvironment<*>) {
|
||||||
if (shouldForwardMessage(env)) {
|
if (shouldForwardMessage(env)) {
|
||||||
forwardPhotoMessage(env)
|
forwardMessage(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
reactToMessage(env, "👀")
|
reactToMessage(env, "👀")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldForwardMessage(env: MediaHandlerEnvironment<List<PhotoSize>>): Boolean {
|
private fun shouldForwardMessage(env: MediaHandlerEnvironment<*>): Boolean {
|
||||||
return env.message.chat.id != botProps.forwardChatId
|
return env.message.chat.id != botProps.forwardChatId
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun forwardPhotoMessage(env: MediaHandlerEnvironment<List<PhotoSize>>) {
|
private suspend fun forwardMessage(env: MediaHandlerEnvironment<*>) {
|
||||||
env.bot
|
env.bot
|
||||||
.forwardMessage(
|
.forwardMessage(
|
||||||
chatId = ChatId.fromId(botProps.forwardChatId),
|
chatId = ChatId.fromId(botProps.forwardChatId),
|
||||||
@@ -38,10 +37,7 @@ class PhotoHandlerService(private val botProps: BotProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun reactToMessage(
|
private suspend fun reactToMessage(env: MediaHandlerEnvironment<*>, emoji: String) {
|
||||||
env: MediaHandlerEnvironment<List<PhotoSize>>,
|
|
||||||
emoji: String,
|
|
||||||
) {
|
|
||||||
env.bot
|
env.bot
|
||||||
.setMessageReaction(
|
.setMessageReaction(
|
||||||
chatId = ChatId.fromId(env.message.chat.id),
|
chatId = ChatId.fromId(env.message.chat.id),
|
||||||
@@ -2,10 +2,12 @@ package com.pischule.memevizor.bot.handler
|
|||||||
|
|
||||||
import com.github.kotlintelegrambot.dispatcher.handlers.MessageHandlerEnvironment
|
import com.github.kotlintelegrambot.dispatcher.handlers.MessageHandlerEnvironment
|
||||||
import com.github.kotlintelegrambot.entities.ChatId
|
import com.github.kotlintelegrambot.entities.ChatId
|
||||||
|
import com.github.kotlintelegrambot.entities.Message
|
||||||
import com.github.kotlintelegrambot.entities.reaction.ReactionType
|
import com.github.kotlintelegrambot.entities.reaction.ReactionType
|
||||||
import com.pischule.memevizor.bot.BotProps
|
import com.pischule.memevizor.bot.BotProps
|
||||||
import com.pischule.memevizor.upload.FileUploaderService
|
import com.pischule.memevizor.upload.FileUploaderService
|
||||||
import com.pischule.memevizor.util.getMaxResPhotoId
|
import com.pischule.memevizor.util.getMaxResPhotoId
|
||||||
|
import com.pischule.memevizor.util.getVideoFileId
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import io.github.oshai.kotlinlogging.withLoggingContext
|
import io.github.oshai.kotlinlogging.withLoggingContext
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
@@ -22,24 +24,34 @@ class ThisCommandHandlerService(
|
|||||||
suspend fun create(env: MessageHandlerEnvironment) {
|
suspend fun create(env: MessageHandlerEnvironment) {
|
||||||
if (!shouldHandleMessage(env)) return
|
if (!shouldHandleMessage(env)) return
|
||||||
|
|
||||||
val maxResPhotoId = env.message.replyToMessage?.getMaxResPhotoId() ?: return
|
val replyToMessage = env.message.replyToMessage ?: return
|
||||||
|
|
||||||
withLoggingContext("file_id" to maxResPhotoId) {
|
val (fileId, contentType) = getFileInfo(replyToMessage) ?: return
|
||||||
val fileBytes = env.bot.downloadFileBytes(maxResPhotoId) ?: return
|
|
||||||
logger.info { "Downloaded a file from Telegram" }
|
|
||||||
|
|
||||||
fileUploaderService.uploadFile(fileBytes, "_.jpeg", "image/jpeg")
|
withLoggingContext("file_id" to fileId) {
|
||||||
|
val fileBytes = env.bot.downloadFileBytes(fileId) ?: return
|
||||||
|
logger.info { "Downloaded a file from Telegram, size=${fileBytes.size}" }
|
||||||
|
|
||||||
|
fileUploaderService.uploadFile(fileBytes, "_", contentType)
|
||||||
|
|
||||||
reactToMessage(env, "👍")
|
reactToMessage(env, "👍")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getFileInfo(message: Message): Pair<String, String>? {
|
||||||
|
return message.getMaxResPhotoId()?.let { it to "image/jpeg" }
|
||||||
|
?: message.getVideoFileId()?.let { it to "video/mp4" }
|
||||||
|
}
|
||||||
|
|
||||||
private fun shouldHandleMessage(env: MessageHandlerEnvironment): Boolean {
|
private fun shouldHandleMessage(env: MessageHandlerEnvironment): Boolean {
|
||||||
val isApprover = env.message.from?.id?.let { botProps.approverUserIds.contains(it) } == true
|
val isApprover = env.message.from?.id?.let { botProps.approverUserIds.contains(it) } == true
|
||||||
val command = env.message.text?.lowercase()
|
val command = env.message.text?.lowercase()
|
||||||
val isConfirmCommand = command in confirmCommands
|
val isConfirmCommand = command in confirmCommands
|
||||||
val hasPhotoReply = env.message.replyToMessage?.photo?.isNotEmpty() == true
|
val hasMediaReply =
|
||||||
return isApprover && isConfirmCommand && hasPhotoReply
|
env.message.replyToMessage?.let {
|
||||||
|
it.photo?.isNotEmpty() == true || it.video != null
|
||||||
|
} == true
|
||||||
|
return isApprover && isConfirmCommand && hasMediaReply
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun reactToMessage(env: MessageHandlerEnvironment, emoji: String) {
|
private suspend fun reactToMessage(env: MessageHandlerEnvironment, emoji: String) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ private val logger = KotlinLogging.logger {}
|
|||||||
@Service
|
@Service
|
||||||
class DummyFileUploadService() : FileUploaderService {
|
class DummyFileUploadService() : FileUploaderService {
|
||||||
|
|
||||||
override suspend fun uploadFile(fileBytes: ByteArray, filename: String, contentType: String) {
|
override fun uploadFile(fileBytes: ByteArray, filename: String, contentType: String) {
|
||||||
logger.info { "File $filename has been successfully uploaded to nowhere" }
|
logger.info { "File $filename has been successfully uploaded to nowhere" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ import org.springframework.stereotype.Service
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
interface FileUploaderService {
|
interface FileUploaderService {
|
||||||
suspend fun uploadFile(fileBytes: ByteArray, filename: String, contentType: String)
|
fun uploadFile(fileBytes: ByteArray, filename: String, contentType: String)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.pischule.memevizor.upload
|
package com.pischule.memevizor.upload
|
||||||
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.springframework.boot.context.event.ApplicationStartedEvent
|
import org.springframework.boot.context.event.ApplicationStartedEvent
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.context.event.EventListener
|
import org.springframework.context.event.EventListener
|
||||||
@@ -17,7 +16,7 @@ class IndexInitializer(val fileUploaderService: FileUploaderService) {
|
|||||||
fun applicationStartedHandler(event: ApplicationStartedEvent) {
|
fun applicationStartedHandler(event: ApplicationStartedEvent) {
|
||||||
try {
|
try {
|
||||||
val fileBytes = readResourceAsByteArray("static/index.html")
|
val fileBytes = readResourceAsByteArray("static/index.html")
|
||||||
runBlocking { fileUploaderService.uploadFile(fileBytes, "index.html", "text/html") }
|
fileUploaderService.uploadFile(fileBytes, "index.html", "text/html")
|
||||||
} catch (e: Error) {
|
} catch (e: Error) {
|
||||||
logger.warn(e) { "Failed to upload " }
|
logger.warn(e) { "Failed to upload " }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.pischule.memevizor.upload
|
package com.pischule.memevizor.upload
|
||||||
|
|
||||||
import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider
|
import io.minio.MinioClient
|
||||||
import aws.sdk.kotlin.services.s3.S3Client
|
|
||||||
import aws.smithy.kotlin.runtime.net.url.Url
|
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
@@ -13,14 +11,10 @@ import org.springframework.context.annotation.Profile
|
|||||||
@Configuration
|
@Configuration
|
||||||
class S3Config {
|
class S3Config {
|
||||||
@Bean
|
@Bean
|
||||||
fun s3Client(s3Props: S3Props): S3Client {
|
fun s3Client(s3Props: S3Props): MinioClient =
|
||||||
return S3Client {
|
MinioClient.builder()
|
||||||
endpointUrl = Url.parse("https://storage.yandexcloud.net")
|
.endpoint("https://storage.yandexcloud.net")
|
||||||
region = "ru-central1"
|
.region("ru-central1")
|
||||||
credentialsProvider = StaticCredentialsProvider {
|
.credentials(s3Props.accessKeyId, s3Props.secretAccessKey)
|
||||||
accessKeyId = s3Props.accessKeyId
|
.build()
|
||||||
secretAccessKey = s3Props.secretAccessKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.pischule.memevizor.upload
|
package com.pischule.memevizor.upload
|
||||||
|
|
||||||
import aws.sdk.kotlin.services.s3.S3Client
|
|
||||||
import aws.sdk.kotlin.services.s3.model.PutObjectRequest
|
|
||||||
import aws.smithy.kotlin.runtime.content.ByteStream
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import io.minio.MinioClient
|
||||||
|
import io.minio.PutObjectArgs
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
@@ -11,16 +10,17 @@ private val logger = KotlinLogging.logger {}
|
|||||||
|
|
||||||
@ConditionalOnBean(S3Config::class)
|
@ConditionalOnBean(S3Config::class)
|
||||||
@Service
|
@Service
|
||||||
class S3FileUploaderService(private val s3Client: S3Client, private val s3Props: S3Props) :
|
class S3FileUploaderService(private val s3Client: MinioClient, private val s3Props: S3Props) :
|
||||||
FileUploaderService {
|
FileUploaderService {
|
||||||
override suspend fun uploadFile(fileBytes: ByteArray, filename: String, contentType: String) {
|
override fun uploadFile(fileBytes: ByteArray, filename: String, contentType: String) {
|
||||||
|
logger.info { "Before upload" }
|
||||||
s3Client.putObject(
|
s3Client.putObject(
|
||||||
PutObjectRequest {
|
PutObjectArgs.builder()
|
||||||
body = ByteStream.fromBytes(fileBytes)
|
.bucket(s3Props.bucket)
|
||||||
bucket = s3Props.bucket
|
.`object`(filename)
|
||||||
key = filename
|
.stream(fileBytes.inputStream(), fileBytes.size.toLong(), -1)
|
||||||
this.contentType = contentType
|
.contentType(contentType)
|
||||||
}
|
.build()
|
||||||
)
|
)
|
||||||
logger.info { "File $filename has been uploaded to S3" }
|
logger.info { "File $filename has been uploaded to S3" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,3 +3,5 @@ package com.pischule.memevizor.util
|
|||||||
import com.github.kotlintelegrambot.entities.Message
|
import com.github.kotlintelegrambot.entities.Message
|
||||||
|
|
||||||
fun Message.getMaxResPhotoId(): String? = this.photo?.lastOrNull()?.fileId
|
fun Message.getMaxResPhotoId(): String? = this.photo?.lastOrNull()?.fileId
|
||||||
|
|
||||||
|
fun Message.getVideoFileId(): String? = this.video?.fileId
|
||||||
|
|||||||
@@ -27,22 +27,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: var(--bg-image);
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
filter: blur(20px);
|
|
||||||
transform: scale(1.1);
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -51,57 +35,52 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code {
|
video {
|
||||||
position: fixed;
|
width: 100%;
|
||||||
z-index: 2;
|
height: 100%;
|
||||||
bottom: 20px;
|
object-fit: contain;
|
||||||
right: 20px;
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<img id="image" alt="Смешная картинка" src="">
|
<img id="image" alt="Смешная картинка" src="">
|
||||||
<img id="qr-code" class="qr-code" alt="QR Code" src="qr.svg" onerror="this.style.display='none'">
|
|
||||||
</body>
|
</body>
|
||||||
<script defer>
|
<script defer>
|
||||||
const imageUrl = '_.jpeg';
|
const fileUrl = '_';
|
||||||
const refreshIntervalMs = 30_000; // Time in milliseconds (e.g., 10000 = 10 seconds)
|
const refreshIntervalMs = 30_000; // Time in milliseconds (e.g., 10000 = 10 seconds)
|
||||||
|
|
||||||
function configureQrCode() {
|
|
||||||
// Parse query parameters for QR code size
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const qrSize = parseInt(urlParams.get('qrSize')) || 100; // Default to 100px if not specified
|
|
||||||
const qrOffset = parseInt(urlParams.get('qrOffset')) || 20; // Default to 20px if not specified
|
|
||||||
|
|
||||||
const qrCodeElement = document.getElementById('qr-code');
|
|
||||||
qrCodeElement.style.width = `${qrSize}px`;
|
|
||||||
qrCodeElement.style.height = `${qrSize}px`;
|
|
||||||
qrCodeElement.style.bottom = `${qrOffset}px`;
|
|
||||||
qrCodeElement.style.right = `${qrOffset}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure QR code on page load
|
|
||||||
configureQrCode();
|
|
||||||
|
|
||||||
let lastModified = null;
|
let lastModified = null;
|
||||||
const imageElement = document.getElementById('image');
|
|
||||||
let imageBlobUrl = null;
|
|
||||||
|
|
||||||
function updateBackgroundImage(imageUrl) {
|
const mediaContainer = document.querySelector("body")
|
||||||
document.documentElement.style.setProperty(
|
|
||||||
'--bg-image',
|
async function refreshMediaDom(response) {
|
||||||
`linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('${imageUrl}')`
|
const blob = await response.blob();
|
||||||
);
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
const contentType = response.headers.get("content-type");
|
||||||
|
if (contentType === "video/mp4") {
|
||||||
|
const video = document.createElement("video");
|
||||||
|
video.src = blobUrl;
|
||||||
|
video.controls = true;
|
||||||
|
video.muted = true;
|
||||||
|
video.loop = true;
|
||||||
|
video.autoplay = true;
|
||||||
|
mediaContainer.replaceChildren(video);
|
||||||
|
} else {
|
||||||
|
const img = document.createElement("img");
|
||||||
|
img.src = blobUrl;
|
||||||
|
mediaContainer.replaceChildren(img);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshImage() {
|
async function refreshMedia() {
|
||||||
try {
|
try {
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
if (lastModified != null) {
|
if (lastModified != null) {
|
||||||
headers.append('If-Modified-Since', lastModified);
|
headers.append('If-Modified-Since', lastModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(imageUrl, {method: 'GET', headers, cache: 'no-store'});
|
const response = await fetch(fileUrl, {method: 'GET', headers, cache: 'no-store'});
|
||||||
const now = new Date().toLocaleTimeString();
|
const now = new Date().toLocaleTimeString();
|
||||||
|
|
||||||
if (response.status === 304) {
|
if (response.status === 304) {
|
||||||
@@ -113,10 +92,7 @@
|
|||||||
const newLastModified = response.headers.get('last-modified');
|
const newLastModified = response.headers.get('last-modified');
|
||||||
if (newLastModified) {
|
if (newLastModified) {
|
||||||
lastModified = newLastModified;
|
lastModified = newLastModified;
|
||||||
const imageBlob = await response.blob();
|
await refreshMediaDom(response)
|
||||||
imageBlobUrl = URL.createObjectURL(imageBlob);
|
|
||||||
imageElement.src = imageBlobUrl;
|
|
||||||
updateBackgroundImage(imageBlobUrl)
|
|
||||||
} else {
|
} else {
|
||||||
console.warn(`No Last-Modified header found. Cannot perform conditional checks`);
|
console.warn(`No Last-Modified header found. Cannot perform conditional checks`);
|
||||||
}
|
}
|
||||||
@@ -129,13 +105,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshImage();
|
refreshMedia();
|
||||||
setInterval(refreshImage, refreshIntervalMs);
|
setInterval(refreshMedia, refreshIntervalMs);
|
||||||
document.addEventListener('click', refreshImage);
|
document.addEventListener('click', refreshMedia);
|
||||||
document.addEventListener('keydown', function (event) {
|
document.addEventListener('keydown', function (event) {
|
||||||
if (event.code === 'Space') {
|
if (event.code === 'Space') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
refreshImage();
|
refreshMedia();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user