Code cleanup, following and gif support

This commit is contained in:
Pablo Ferreiro 2022-01-06 00:11:00 +01:00
parent 493f56a052
commit 30954f3d3a
No known key found for this signature in database
GPG key ID: 41FBCE65B779FA24
21 changed files with 318 additions and 123 deletions

View file

@ -41,7 +41,6 @@ location /tiktok-viewer/.env {
## TODO
* Allow searching for just one video using the ID
* Code cleanup
## Credits
* [TikTok-API-PHP](https://github.com/ssovit/TikTok-API-PHP)

View file

@ -11,6 +11,11 @@
"latte/latte": "^2.10",
"vlucas/phpdotenv": "^5.4"
},
"autoload": {
"psr-4": {
"Helpers\\": "helpers/"
}
},
"scripts": {
"post-install-cmd": [
"php fix_api.php"

54
composer.lock generated
View file

@ -70,16 +70,16 @@
},
{
"name": "latte/latte",
"version": "v2.10.7",
"version": "v2.10.8",
"source": {
"type": "git",
"url": "https://github.com/nette/latte.git",
"reference": "a69d0b9598652438b5754ae5c1abc217d5003d98"
"reference": "596b28bf098ebb852732d60b00538139a009c4db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/latte/zipball/a69d0b9598652438b5754ae5c1abc217d5003d98",
"reference": "a69d0b9598652438b5754ae5c1abc217d5003d98",
"url": "https://api.github.com/repos/nette/latte/zipball/596b28bf098ebb852732d60b00538139a009c4db",
"reference": "596b28bf098ebb852732d60b00538139a009c4db",
"shasum": ""
},
"require": {
@ -148,9 +148,9 @@
],
"support": {
"issues": "https://github.com/nette/latte/issues",
"source": "https://github.com/nette/latte/tree/v2.10.7"
"source": "https://github.com/nette/latte/tree/v2.10.8"
},
"time": "2021-12-21T11:22:49+00:00"
"time": "2022-01-04T14:13:28+00:00"
},
{
"name": "phpoption/phpoption",
@ -314,21 +314,24 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.23.0",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
@ -373,7 +376,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0"
},
"funding": [
{
@ -389,25 +392,28 @@
"type": "tidelift"
}
],
"time": "2021-02-19T12:13:01+00:00"
"time": "2021-10-20T20:35:02+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.23.1",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6"
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6",
"reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
@ -453,7 +459,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0"
},
"funding": [
{
@ -469,20 +475,20 @@
"type": "tidelift"
}
],
"time": "2021-05-27T12:26:48+00:00"
"time": "2021-11-30T18:21:41+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.23.1",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be"
"reference": "57b712b08eddb97c762a8caa32c84e037892d2e9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be",
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9",
"reference": "57b712b08eddb97c762a8caa32c84e037892d2e9",
"shasum": ""
},
"require": {
@ -536,7 +542,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0"
},
"funding": [
{
@ -552,7 +558,7 @@
"type": "tidelift"
}
],
"time": "2021-07-28T13:41:28+00:00"
"time": "2021-09-13T13:58:33+00:00"
},
{
"name": "vlucas/phpdotenv",

12
helpers/Following.php Normal file
View file

@ -0,0 +1,12 @@
<?php
namespace Helpers;
class Following {
static public function get (): array {
$following_string = Settings::get('following');
if ($following_string) {
return explode(',', $following_string);
}
return [];
}
};

54
helpers/Misc.php Normal file
View file

@ -0,0 +1,54 @@
<?php
namespace Helpers;
use Helpers\Settings;
class Misc {
static public function getSubDir(): string {
return isset($_ENV['APP_SUBDIR']) && !empty($_ENV['APP_SUBDIR']) ? $_ENV['APP_SUBDIR'] : '';
}
static public function getView(string $template): string {
return __DIR__ . "/../views/{$template}.latte";
}
static public function api(): \Sovit\TikTok\Api {
$options = [];
// Proxy config
if (in_array(Settings::$proxy, $_COOKIE)) {
foreach (Settings::$proxy as $proxy_element) {
$options[$proxy_element] = $_COOKIE[$proxy_element];
}
}
$api = new \Sovit\TikTok\Api($options);
return $api;
}
static public function latte(): \Latte\Engine {
$subdir = Misc::getSubDir();
$latte = new \Latte\Engine;
$latte->setTempDirectory(__DIR__ . '/../cache/views');
$latte->addFunction('assets', function (string $name, string $type) use ($subdir) {
$path = "{$subdir}/{$type}/{$name}";
return $path;
});
$latte->addFunction('path', function (string $name) use ($subdir) {
$path = "{$subdir}/{$name}";
return $path;
});
// https://stackoverflow.com/a/36365553
$latte->addFunction('number', function (int $x) {
if($x > 1000) {
$x_number_format = number_format($x);
$x_array = explode(',', $x_number_format);
$x_parts = array('K', 'M', 'B', 'T');
$x_count_parts = count($x_array) - 1;
$x_display = $x;
$x_display = $x_array[0] . ((int) $x_array[1][0] !== 0 ? '.' . $x_array[1][0] : '');
$x_display .= $x_parts[$x_count_parts - 1];
return $x_display;
}
return $x;
});
return $latte;
}
}

21
helpers/Settings.php Normal file
View file

@ -0,0 +1,21 @@
<?php
namespace Helpers;
class Settings {
static public $proxy = ['proxy-host', 'proxy-port', 'proxy-username', 'proxy-password'];
static public function get(string $name): string {
if (isset($_COOKIE[$name]) && !empty($_COOKIE[$name])) {
return $_COOKIE[$name];
}
return '';
}
static public function exists(string $name): bool {
return isset($_COOKIE[$name]);
}
static public function set(string $name, string $value) {
setcookie($name, $value, time()+60*60*24*30, '', '', true, true);
}
};

View file

@ -1,2 +0,0 @@
<?php
$proxy_elements = ['proxy-host', 'proxy-port', 'proxy-username', 'proxy-password'];

View file

@ -1,16 +1,12 @@
<?php
require __DIR__ . "/vendor/autoload.php";
use Steampixel\Route;
use Helpers\Misc;
// LOAD DOTENV
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->safeLoad();
// -- HELPERS -- //
function getSubdir(): string {
return isset($_ENV['APP_SUBDIR']) && !empty($_ENV['APP_SUBDIR']) ? $_ENV['APP_SUBDIR'] : '';
}
require __DIR__ . '/routes/index.php';
Route::run(getSubdir());
Route::run(Misc::getSubDir());

32
routes/following.php Normal file
View file

@ -0,0 +1,32 @@
<?php
use Helpers\Following;
use Helpers\Misc;
use Steampixel\Route;
// Showing
Route::add('/following', function () {
$allowed_items_total = isset($_GET['max']) && is_numeric($_GET['max']) && $_GET['max'] <= 100 ? $_GET['max'] : 20;
$following = Following::get();
$items = [];
if (count($following) !== 0) {
$api = Misc::api();
$max_items_per_user = $allowed_items_total / count($following);
foreach ($following as $user) {
$user_feed = $api->getUserFeed($user);
if ($user_feed) {
$max = count($user_feed->items) > $max_items_per_user ? $max_items_per_user : count($user_feed->items);
for ($i = 0; $i < $max; $i++) {
$item = $user_feed->items[$i];
array_push($items, $item);
}
}
}
}
$feed = (object) [
'items' => $items,
'hasMore' => false
];
$latte = Misc::latte();
$latte->render(Misc::getView('following'), ['following' => $following, 'feed' => $feed]);
});

View file

@ -1,104 +1,59 @@
<?php
require __DIR__ . '/assets.php';
require __DIR__ . '/settings.php';
require __DIR__ . "/../helpers/settings_elements.php";
require __DIR__ . '/following.php';
use Steampixel\Route;
// -- ROUTING HELPERS -- //
function getApi(array $proxy_elements): \Sovit\TikTok\Api {
$options = [];
// Proxy config
if (in_array($proxy_elements, $_COOKIE)) {
foreach ($proxy_elements as $proxy_element) {
$options[$proxy_element] = $_COOKIE[$proxy_element];
}
}
$api = new \Sovit\TikTok\Api($options);
return $api;
}
function getLatte(): \Latte\Engine {
$subdir = getSubdir();
$latte = new Latte\Engine;
$latte->setTempDirectory(__DIR__ . '/../cache/views');
$latte->addFunction('assets', function (string $name, string $type) use ($subdir) {
$path = "{$subdir}/{$type}/{$name}";
return $path;
});
$latte->addFunction('path', function (string $name) use ($subdir) {
$path = "{$subdir}/{$name}";
return $path;
});
// https://stackoverflow.com/a/36365553
$latte->addFunction('number', function (int $x) {
if($x > 1000) {
$x_number_format = number_format($x);
$x_array = explode(',', $x_number_format);
$x_parts = array('K', 'M', 'B', 'T');
$x_count_parts = count($x_array) - 1;
$x_display = $x;
$x_display = $x_array[0] . ((int) $x_array[1][0] !== 0 ? '.' . $x_array[1][0] : '');
$x_display .= $x_parts[$x_count_parts - 1];
return $x_display;
}
return $x;
});
return $latte;
}
function getView(string $template): string {
return __DIR__ . "/../views/{$template}.latte";
}
use Helpers\Misc;
Route::add('/', function () {
$latte = getLatte();
$latte->render(getView('home'));
$latte = Misc::latte();
$latte->render(Misc::getView('home'));
});
Route::add("/trending", function () use ($proxy_elements) {
Route::add("/trending", function () {
$cursor = 0;
if (isset($_GET['cursor']) && is_numeric($_GET['cursor'])) {
$cursor = (int) $_GET['cursor'];
}
$latte = getLatte();
$api = getApi($proxy_elements);
$latte = Misc::latte();
$api = Misc::api();
$feed = $api->getTrendingFeed($cursor);
if ($feed) {
$latte->render(getView('trending'), ['feed' => $feed]);
$latte->render(Misc::getView('trending'), ['feed' => $feed]);
} else {
return 'ERROR!';
}
});
Route::add("/@([^/]+)", function (string $username) use ($proxy_elements) {
Route::add("/@([^/]+)", function (string $username) {
$cursor = 0;
if (isset($_GET['cursor']) && is_numeric($_GET['cursor'])) {
$cursor = (int) $_GET['cursor'];
}
$latte = getLatte();
$api = getApi($proxy_elements);
$latte = Misc::latte();
$api = Misc::api();
$feed = $api->getUserFeed($username, $cursor);
if ($feed) {
if ($feed->info->detail->user->privateAccount) {
http_response_code(400);
return 'Private account detected! Not supported';
}
$latte->render(getView('user'), ['feed' => $feed]);
$latte->render(Misc::getView('user'), ['feed' => $feed]);
} else {
return 'ERROR!';
}
});
Route::add('/tag/(\w+)', function (string $name) use ($proxy_elements) {
Route::add('/tag/(\w+)', function (string $name) {
$cursor = 0;
if (isset($_GET['cursor']) && is_numeric($_GET['cursor'])) {
$cursor = (int) $_GET['cursor'];
}
$latte = getLatte();
$api = getApi($proxy_elements);
$latte = Misc::latte();
$api = Misc::api();
$feed = $api->getChallengeFeed($name, $cursor);
if ($feed) {
$latte->render(getView('tag'), ['feed' => $feed]);
$latte->render(Misc::getView('tag'), ['feed' => $feed]);
} else {
return 'ERROR!';
}

View file

@ -1,18 +1,43 @@
<?php
require __DIR__ . "/../helpers/settings_elements.php";
use Helpers\Following;
use Helpers\Settings;
use Helpers\Misc;
use Steampixel\Route;
Route::add("/settings", function () use ($proxy_elements) {
$latte = getLatte();
$latte->render(getView('settings'), ["proxy_elements" => $proxy_elements]);
Route::add("/settings", function () {
$latte = Misc::latte();
$latte->render(Misc::getView('settings'), ["proxy_elements" => Settings::$proxy, "following" => Following::get()]);
});
Route::add("/settings", function () use ($proxy_elements) {
if (in_array($proxy_elements, $_POST)) {
foreach ($proxy_elements as $proxy_element) {
setcookie($proxy_element, $_POST[$proxy_element], time()+60*60*24*30, '/', '', true, true);
Route::add("/settings/proxy", function () {
if (in_array(Settings::$proxy, $_POST)) {
foreach (Settings::$proxy as $proxy_element) {
Settings::set($proxy_element, $_POST[$proxy_element]);
}
}
http_response_code(302);
header('Location: ./home');
}, 'POST');
Route::add("/settings/following", function () {
$following = Following::get();
if (isset($_POST['add'])) {
// Add following
array_push($following, $_POST['account']);
} elseif (isset($_POST['remove'])) {
$index = array_search($_POST['account'], $following);
if ($index !== false) {
unset($following[$index]);
}
} else {
return 'You need to send a mode!';
}
// Build string
$following_string = implode(',', $following);
Settings::set('following', $following_string);
header('Location: ../settings');
}, 'POST');

View file

@ -101,10 +101,32 @@ const hashChange = () => {
}
}
const swapImages = (e, mode) => {
if (mode === 'gif') {
const a = e.target
const img = a.children[0]
const gif = a.children[1]
img.classList.add('hidden')
gif.classList.remove('hidden')
} else if (mode === 'img') {
const gif = e.target
const img = e.target.parentElement.children[0]
img.classList.remove('hidden')
gif.classList.add('hidden')
}
}
document.getElementById('modal-background').addEventListener('click', hideModel, false)
document.getElementById('modal-close').addEventListener('click', hideModel, false)
document.getElementById('back-button').addEventListener('click', () => moveVideo(false))
document.getElementById('next-button').addEventListener('click', () => moveVideo(true))
window.addEventListener('hashchange', hashChange, false)
// Image hover
const images = document.getElementsByClassName("clickable-img")
for (let i = 0; i < images.length; i++) {
images[i].addEventListener('mouseenter', e => swapImages(e, 'gif'), false)
images[i].addEventListener('mouseout', e => swapImages(e, 'img'), false)
}
hashChange()

View file

@ -3,6 +3,12 @@
overflow: hidden;
}
.clickable-img {
cursor: pointer;
.hidden {
display: none;
}
/* Make gifs take all available space */
.clickable-img img {
width: 100%;
height: 100%;
}

View file

@ -3,7 +3,7 @@
<div class="columns is-multiline is-vcentered">
{foreach $feed->items as $item}
<div class="column is-one-quarter">
<a id="{$item->id}" href="#{$item->id}" class="clickable-img"
<a class="clickable-img" id="{$item->id}" href="#{$item->id}"
data-video_url="{path('stream?url=' . urlencode($item->video->playAddr))}"
data-video_download="{path('stream?url=' . urlencode($item->video->playAddr) . '&download=1')}"
data-desc="{$item->desc}"
@ -12,11 +12,12 @@
data-music_title="{$item->music->title}"
data-music_url="{path('stream?url=' . urlencode($item->music->playUrl))}">
<img loading="lazy" src="{path('stream?url=' . urlencode($item->video->originCover))}" />
<img class="hidden" loading="lazy" src="{path('stream?url=' . urlencode($item->video->dynamicCover))}" />
</a>
</div>
{/foreach}
</div>
<div class="buttons">
<div n:ifset="$feed->info" class="buttons">
<a n:ifset="$_GET['cursor']" class="button is-danger" href="?cursor=0">First</a>
<a class="button is-danger" href="?cursor={$feed->minCursor}">Back</a>
{if $feed->hasMore}

View file

@ -0,0 +1,9 @@
<div class="tags">
{if !empty($following)}
{foreach $following as $user}
<span class="tag">{$user}</span>
{/foreach}
{else}
<p>None</p>
{/if}
</div>

View file

@ -10,6 +10,7 @@
<div id="navbar-menu" class="navbar-menu">
<div class="navbar-start">
<a href="{path('')}" class="navbar-item">Home</a>
<a href="{path('following')}" class="navbar-item">Following</a>
<a href="{path('settings')}" class="navbar-item">Settings</a>
</div>
</div>

View file

@ -0,0 +1,24 @@
{include '../following_tags.latte'}
<form action="./settings/following" method="POST">
<div class="field">
<label class="label">Add / Remove user</label>
<div class="control">
<input name="account" class="input" type="text" />
</div>
</div>
<div class="field">
<div class="control">
<label class="radio">
<input type="radio" name="add">Add
</label>
<label class="radio">
<input type="radio" name="remove">Remove
</label>
</div>
</div>
<div class="field">
<div class="control">
<button class="button is-primary" type="submit">Send</button>
</div>
</div>
</form>

View file

@ -0,0 +1,15 @@
<form action="./settings/proxy" method="POST">
{foreach $proxy_elements as $proxy_element}
<div class="field">
<label class="label">{$proxy_element}</label>
<div class="control">
<input name="{$proxy_element}" class="input" value="{isset($_COOKIE[$proxy_element]) ? $_COOKIE[$proxy_element] : ''}" required />
</div>
</div>
{/foreach}
<div class="field">
<div class="control">
<button class="button is-success" type="submit">Submit</button>
</div>
</div>
</form>

24
views/following.latte Normal file
View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
{include 'components/head.latte', title: 'Following'}
<body>
{include 'components/navbar.latte'}
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<p class="title">Following</p>
</div>
</div>
</section>
<section class="section">
<p class="title">Following:</p>
{include 'components/following_tags.latte'}
<p>You can add/remove follows on settings</p>
</section>
<hr />
{include 'components/feed.latte'}
{include 'components/footer.latte'}
</body>
</html>

View file

@ -15,21 +15,11 @@
<section class="section">
<!-- Proxy settings -->
<p class="title">Proxy</p>
<form action="./settings" method="POST">
{foreach $proxy_elements as $proxy_element}
<div class="field">
<label class="label">{$proxy_element}</label>
<div class="control">
<input name="{$proxy_element}" class="input" value="{isset($_COOKIE[$proxy_element]) ? $_COOKIE[$proxy_element] : ''}" required />
</div>
</div>
{/foreach}
<div class="field">
<div class="control">
<button class="button is-success" type="submit">Submit</button>
</div>
</div>
</form>
{include 'components/settings/proxy.latte'}
<hr />
<!-- Following -->
<p class="title">Following</p>
{include 'components/settings/following.latte'}
</section>
{include 'components/footer.latte'}
</body>

View file

@ -8,7 +8,7 @@
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<p class="title">Trending page</p>
<p class="title">Trending</p>
</div>
</div>
</section>