summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--database.sql8
-rw-r--r--lib/config.php3
-rw-r--r--sounds/add.php190
-rw-r--r--static/style.css51
4 files changed, 251 insertions, 1 deletions
diff --git a/database.sql b/database.sql
index 42a6b50..ee3d285 100644
--- a/database.sql
+++ b/database.sql
@@ -20,4 +20,12 @@ CREATE TABLE IF NOT EXISTS sounds (
code TEXT NOT NULL,
uploaded_at TIMESTAMP NOT NULL DEFAULT UTC_TIMESTAMP,
uploaded_by BIGINT REFERENCES users(id)
+);
+
+CREATE TABLE IF NOT EXISTS added_sounds (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ sound_id BIGINT NOT NULL REFERENCES sounds(id),
+ emote_id TEXT NOT NULL,
+ added_at TIMESTAMP NOT NULL DEFAULT UTC_TIMESTAMP,
+ UNIQUE (sound_id, emote_id)
); \ No newline at end of file
diff --git a/lib/config.php b/lib/config.php
index bdc399b..e57714e 100644
--- a/lib/config.php
+++ b/lib/config.php
@@ -13,4 +13,7 @@ define('INSTANCE_NAME', $c['instance']['name'] ?? $_SERVER['HTTP_HOST']);
define('SOUND_DIRECTORY', $c['sound']['directory'] ?? "{$_SERVER['DOCUMENT_ROOT']}/static/userdata/sounds");
define('SOUND_DIRECTORY_PREFIX', $c['sound']['directory_prefix'] ?? "/static/userdata/sounds");
+define('EMOTE_FETCH_BETTERTTV', boolval($c['emote']['fetch_betterttv'] ?? '1') ?? true);
+define('EMOTE_FETCH_7TV', boolval($c['emote']['fetch_7tv'] ?? '1') ?? true);
+
define('IS_JSON_REQUEST', isset($_SERVER['HTTP_ACCEPT']) && str_contains($_SERVER['HTTP_ACCEPT'], 'application/json')); \ No newline at end of file
diff --git a/sounds/add.php b/sounds/add.php
new file mode 100644
index 0000000..9a008de
--- /dev/null
+++ b/sounds/add.php
@@ -0,0 +1,190 @@
+<?php
+include_once $_SERVER['DOCUMENT_ROOT'] . '/lib/config.php';
+include_once $_SERVER['DOCUMENT_ROOT'] . '/lib/partials.php';
+include_once $_SERVER['DOCUMENT_ROOT'] . '/lib/users.php';
+
+authenticate_user();
+
+$db = new PDO(DB_URL, DB_USER, DB_PASS);
+
+if (isset($_GET['id']) && !empty(trim($_GET['id']))) {
+ $stmt = $db->prepare('SELECT id, code
+ FROM sounds
+ WHERE id = ?
+ ');
+ $stmt->execute([$_GET['id']]);
+ $sound = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
+
+ // TODO: check if user has twitch connection
+ $twitch = $_SESSION['user']['connections'][0];
+
+ if ($sound) {
+ $emotes = [];
+
+ if (EMOTE_FETCH_BETTERTTV) {
+ // fetching channel emotes
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, "https://api.betterttv.net/3/cached/users/twitch/" . $twitch['user_alias_id']);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ // parsing channel emotes
+ $response = json_decode($response, true);
+ $bttvemotes = [];
+ foreach ($response['channelEmotes'] as $e) {
+ array_push($bttvemotes, [
+ 'code' => $e['code'],
+ 'eid' => 'betterttv:' . $e['id'],
+ 'url' => 'https://cdn.betterttv.net/emote/' . $e['id'] . '/2x.webp',
+ 'uploader' => $twitch['user_alias_name']
+ ]);
+ }
+ foreach ($response['sharedEmotes'] as $e) {
+ array_push($bttvemotes, [
+ 'code' => $e['code'],
+ 'eid' => 'betterttv:' . $e['id'],
+ 'url' => 'https://cdn.betterttv.net/emote/' . $e['id'] . '/2x.webp',
+ 'uploader' => $e['user']['displayName']
+ ]);
+ }
+
+ // fetching global emotes
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, "https://api.betterttv.net/3/cached/emotes/global");
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ // parsing global emotes
+ $response = json_decode($response, true);
+ foreach ($response as $e) {
+ array_push($bttvemotes, [
+ 'code' => $e['code'],
+ 'eid' => 'betterttv:' . $e['id'],
+ 'url' => 'https://cdn.betterttv.net/emote/' . $e['id'] . '/2x.webp'
+ ]);
+ }
+
+ $emotes['BetterTTV'] = $bttvemotes;
+ }
+
+ if (EMOTE_FETCH_7TV) {
+ // fetching channel emotes
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, "https://7tv.io/v3/users/twitch/" . $twitch['user_alias_id']);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ // parsing channel emotes
+ $response = json_decode($response, true);
+ $stvemotes = [];
+ foreach ($response['emote_set']['emotes'] as $e) {
+ array_push($stvemotes, [
+ 'code' => $e['name'],
+ 'eid' => '7tv:' . $e['id'],
+ 'url' => 'https://cdn.7tv.app/emote/' . $e['id'] . '/2x.webp',
+ 'uploader' => $e['data']['owner']['display_name']
+ ]);
+ }
+
+ // fetching global emotes
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, "https://7tv.io/v3/emote-sets/global");
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ // parsing global emotes
+ $response = json_decode($response, true);
+ foreach ($response['emotes'] as $e) {
+ array_push($stvemotes, [
+ 'code' => $e['name'],
+ 'eid' => '7tv:' . $e['id'],
+ 'url' => 'https://cdn.7tv.app/emote/' . $e['id'] . '/2x.webp',
+ 'uploader' => $e['data']['owner']['display_name']
+ ]);
+ }
+
+ $emotes['7TV'] = $stvemotes;
+ }
+ }
+} else {
+ $stmt = $db->prepare('SELECT * FROM sounds ORDER BY uploaded_at DESC');
+ $stmt->execute();
+ $sounds = $stmt->fetchAll(PDO::FETCH_ASSOC);
+}
+?>
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Add sound - <?= INSTANCE_NAME ?></title>
+ <link rel="stylesheet" href="/static/style.css">
+ <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
+</head>
+
+<body>
+ <?php html_header() ?>
+ <main class="row gap-8">
+ <section class="column gap-8">
+ <div class="box">
+ <div class="tab">
+ <p>Adding sound - <?= $sound['code'] ?></p>
+ </div>
+ <div class="content column justify-center align-center gap-16">
+ <audio controls>
+ <source src="<?= sprintf("%s/%d.ogg", SOUND_DIRECTORY_PREFIX, $sound['id']) ?>"
+ type="audio/ogg">
+ </audio>
+ </div>
+ </div>
+ <div class="box" id="chat-preview">
+ <div class="tab">
+ <p>Preview</p>
+ </div>
+ <div class="content chat-preview">
+ <p>wip</p>
+ </div>
+ </div>
+ </section>
+ <section class="column gap-8">
+ <?php foreach ($emotes as $pname => $pe): ?>
+ <div class="box">
+ <div class="tab">
+ <p><?= $pname ?></p>
+ </div>
+ <div class="content row wrap gap-8">
+ <?php foreach ($pe as $e): ?>
+ <a href="/sounds/add.php?id=<?= $sound['id'] ?>&eid=<?= $e['eid'] ?>">
+ <div class="box emote-item">
+ <div class="icon">
+ <img src="<?= $e['url'] ?>" alt="<?= $e['code'] ?>" loading="lazy">
+ </div>
+ <p class="code" title="<?= $e['code'] ?>"><?= $e['code'] ?></p>
+ <?php if (isset($e['uploader'])): ?>
+ <p class="author" title="by <?= $e['uploader'] ?>">by <?= $e['uploader'] ?></p>
+ <?php endif; ?>
+ </div>
+ </a>
+ <?php endforeach; ?>
+ <?php if (empty($pe)): ?>
+ <p><i>No emotes.</i></p>
+ <?php endif; ?>
+ </div>
+ </div>
+ <?php endforeach; ?>
+ </section>
+ </main>
+</body>
+
+<script>
+
+</script>
+
+</html> \ No newline at end of file
diff --git a/static/style.css b/static/style.css
index 6a62cb5..864a4fb 100644
--- a/static/style.css
+++ b/static/style.css
@@ -1,4 +1,5 @@
:root {
+ --primary-0: #81b180;
--primary-1: #97ca96;
--primary-2: #c6eec5;
--primary-3: #d5e9d5;
@@ -77,6 +78,18 @@ header .brand>a {
border-top-right-radius: 4px;
}
+.box>.content:has(.box) {
+ background: var(--primary-0);
+}
+
+.box:has(.box)>.tab {
+ margin-bottom: unset;
+}
+
+a>.box:hover {
+ background: var(--primary-2);
+}
+
.sound-list {
display: flex;
flex-direction: row;
@@ -84,7 +97,8 @@ header .brand>a {
gap: 16px;
}
-.sound-item {
+.sound-item,
+.emote-item {
display: flex;
flex-direction: column;
justify-content: center;
@@ -95,6 +109,37 @@ header .brand>a {
font-size: 12px;
}
+a:has(.emote-item) {
+ text-decoration: none;
+ color: unset;
+}
+
+.emote-item {
+ width: 92px;
+ height: 92px;
+}
+
+.emote-item>.code,
+.emote-item>.author {
+ max-width: 5em;
+ min-height: 1em;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.emote-item>.icon {
+ flex-grow: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.emote-item>.icon>img {
+ max-width: 64px;
+ max-height: 64px;
+}
+
.row {
display: flex;
flex-direction: row;
@@ -105,6 +150,10 @@ header .brand>a {
flex-direction: column;
}
+.wrap {
+ flex-wrap: wrap;
+}
+
.justify-center {
justify-content: center;
}