Templates based on classes, cache custom paths

This commit is contained in:
Pablo Ferreiro 2022-01-25 17:20:11 +01:00
parent dde4185ad7
commit 6d6ec109ca
No known key found for this signature in database
GPG key ID: 41FBCE65B779FA24
26 changed files with 236 additions and 156 deletions

View file

@ -1,7 +1,11 @@
# APP_SUBDIR=/ # Subpath your app is running, defaults to /
# APP_CACHE=redis # Cache engine for TikTok Api, (more info on README)
# APP_SUBDIR=/ # Subpath your app is running, leave commented for /
# Redis, used on Helpers\CahceEngines\RedisCache (CHOOSE ONE)
# LATTE_CACHE=/tmp/proxitok_api # Path for Latte cache, leave commented for ./cache/latte
# API_CACHE=redis # Cache engine for TikTok Api, (more info on README)
# REDIS_URL=redis://:password@host:port # If using password
# REDIS_URL=redis://host:port # If not using password
# Redis cache, used on Helpers\CacheEngines\RedisCache (CHOOSE ONE)
# API_CACHE_REDIS_URL=redis://:password@host:port # If using password
# API_CACHE_REDIS_URL=redis://host:port # If not using password
# JSON cache, used on Helpers\CacheEngines\JSONCache
# API_CACHE_JSON=/tmp/proxitok_api # Path for JSON API Cache, leave commented for ./cache/api

4
.gitignore vendored
View file

@ -2,7 +2,7 @@ node_modules
/.env
/.vscode
/vendor
/cache/views/*
!/cache/views/.gitkeep
/cache/latte/*
!/cache/latte/.gitkeep
/cache/api/*
!/cache/api/.gitkeep

View file

@ -1,7 +1,8 @@
<footer class="footer">
<div class="content">
<p class="has-text-centered">
<div class="content has-text-centered">
<p>
Made with <span style="color: #e25555;">&#9829;</span> in <a href="https://github.com/pablouser1/ProxiTok">Github</a>
</p>
<p>Version: {version()}</p>
</div>
</footer>

View file

@ -1,7 +1,7 @@
{
"name": "pablouser1/proxitok",
"description": "An alternative frontend for TikTok",
"version": "1.1.2",
"version": "1.2.0",
"license": "AGPL-3.0-or-later",
"repositories": [
{
@ -18,7 +18,8 @@
},
"autoload": {
"psr-4": {
"Helpers\\": "helpers/"
"Helpers\\": "helpers/",
"Views\\Models\\": "views/models/"
}
}
}

View file

@ -2,23 +2,30 @@
namespace Helpers\CacheEngines;
class JSONCache {
const CACHE_PATH = __DIR__ . '/../../cache/api/';
private string $cache_path = __DIR__ . '/../../cache/api';
function __construct() {
if (isset($_ENV['API_CACHE_JSON']) && !empty($_ENV['API_CACHE_JSON'])) {
$this->cache_path = $_ENV['API_CACHE_JSON'];
}
}
public function get(string $cache_key): object|false {
if (is_file(self::CACHE_PATH . $cache_key . '.json')) {
$filename = $this->cache_path . '/' . $cache_key . '.json';
if (is_file($filename)) {
$time = time();
$json_string = file_get_contents(self::CACHE_PATH . $cache_key . '.json');
$json_string = file_get_contents($filename);
$element = json_decode($json_string);
if ($time < $element->expires) {
return $element->data;
}
// Remove file if expired
unlink(self::CACHE_PATH . $cache_key . '.json');
unlink($filename);
}
return false;
}
public function set(string $cache_key, mixed $data, $timeout = 3600) {
file_put_contents(self::CACHE_PATH . $cache_key . '.json', json_encode([
file_put_contents($this->cache_path . '/' . $cache_key . '.json', json_encode([
'data' => $data,
'expires' => time() + $timeout
]));

View file

@ -25,8 +25,8 @@ class Misc {
}
}
// Cache config
if (isset($_ENV['APP_CACHE'])) {
switch ($_ENV['APP_CACHE']) {
if (isset($_ENV['API_CACHE'])) {
switch ($_ENV['API_CACHE']) {
case 'json':
$cacheEngine = new JSONCache();
break;
@ -54,15 +54,24 @@ class Misc {
$subdir = '';
}
$latte = new \Latte\Engine;
$latte->setTempDirectory(__DIR__ . '/../cache/views');
$cache_path = isset($_ENV['LATTE_CACHE']) && !empty($_ENV['LATTE_CACHE']) ? $_ENV['LATTE_CACHE'] : __DIR__ . '/../cache/latte';
$latte->setTempDirectory($cache_path);
// -- CUSTOM FUNCTIONS -- //
// Import assets
$latte->addFunction('assets', function (string $name, string $type) use ($subdir) {
$path = "{$subdir}/{$type}/{$name}";
return $path;
});
// Relative path
$latte->addFunction('path', function (string $name) use ($subdir) {
$path = "{$subdir}/{$name}";
return $path;
});
// Version being used
$latte->addFunction('version', function () {
return \Composer\InstalledVersions::getVersion('pablouser1/proxitok');
});
// https://stackoverflow.com/a/36365553
$latte->addFunction('number', function (int $x) {
if($x > 1000) {

View file

@ -2,6 +2,7 @@
use Helpers\Following;
use Helpers\Misc;
use Steampixel\Route;
use Views\Models\FollowingTemplate;
// Showing
Route::add('/following', function () {
@ -28,9 +29,5 @@ Route::add('/following', function () {
'hasMore' => false
];
$latte = Misc::latte();
$latte->render(Misc::getView('following'), [
'following' => $following,
'feed' => $feed,
'title' => 'Following'
]);
$latte->render(Misc::getView('following'), new FollowingTemplate($following, $feed));
});

View file

@ -2,18 +2,22 @@
require __DIR__ . '/assets.php';
require __DIR__ . '/settings.php';
require __DIR__ . '/following.php';
use Steampixel\Route;
use Helpers\Misc;
use Helpers\Error;
use Views\Models\BaseTemplate;
use Views\Models\FeedTemplate;
use Views\Models\ItemTemplate;
Route::add('/', function () {
$latte = Misc::latte();
$latte->render(Misc::getView('home'), ['title' => 'Home']);
$latte->render(Misc::getView('home'), new BaseTemplate('Home'));
});
Route::add('/about', function () {
$latte = Misc::latte();
$latte->render(Misc::getView('about'), ['title' => 'About']);
$latte->render(Misc::getView('about'), new BaseTemplate('About'));
});
Route::add("/trending", function () {
@ -25,10 +29,7 @@ Route::add("/trending", function () {
$feed = $api->getTrendingFeed($cursor);
if ($feed->meta->success) {
$latte = Misc::latte();
$latte->render(Misc::getView('trending'), [
'feed' => $feed,
'title' => 'Trending'
]);
$latte->render(Misc::getView('trending'), new FeedTemplate('Trending', $feed));
} else {
Error::show($feed->meta);
}
@ -47,10 +48,7 @@ Route::add("/@([^/]+)", function (string $username) {
return 'Private account detected! Not supported';
}
$latte = Misc::latte();
$latte->render(Misc::getView('user'), [
'feed' => $feed,
'title' => $feed->info->detail->user->nickname
]);
$latte->render(Misc::getView('user'), new FeedTemplate($feed->info->detail->user->nickname, $feed));
} else {
Error::show($feed->meta);
}
@ -61,10 +59,7 @@ Route::add('/video/([^/]+)', function (string $video_id) {
$item = $api->getVideoByID($video_id);
if ($item->meta->success) {
$latte = Misc::latte();
$latte->render(Misc::getView('video'), [
'item' => $item,
'title' => $item->info->detail->user->nickname
]);
$latte->render(Misc::getView('video'), new ItemTemplate($item->info->detail->user->nickname, $item));
} else {
Error::show($item->meta);
}
@ -80,10 +75,7 @@ Route::add('/music/([^/]+)', function (string $music_id) {
$feed = $api->getMusicFeed($music_id, $cursor);
if ($feed->meta->success) {
$latte = Misc::latte();
$latte->render(Misc::getView('music'), [
'feed' => $feed,
'title' => 'Music'
]);
$latte->render(Misc::getView('music'), new FeedTemplate('Music', $feed));
} else {
Error::show($feed->meta);
}
@ -98,10 +90,7 @@ Route::add('/tag/(\w+)', function (string $name) {
$feed = $api->getChallengeFeed($name, $cursor);
if ($feed->meta->success) {
$latte = Misc::latte();
$latte->render(Misc::getView('tag'), [
'feed' => $feed,
'title' => 'Tag'
]);
$latte->render(Misc::getView('tag'), new FeedTemplate('Tag', $feed));
} else {
Error::show($feed->meta);
}

View file

@ -3,15 +3,12 @@
use Helpers\Following;
use Helpers\Settings;
use Helpers\Misc;
use Views\Models\SettingsTemplate;
use Steampixel\Route;
Route::add("/settings", function () {
$latte = Misc::latte();
$latte->render(Misc::getView('settings'), [
"proxy_elements" => Settings::PROXY,
"following" => Following::get(),
"title" => "Settings"
]);
$latte->render(Misc::getView('settings'), new SettingsTemplate());
});
Route::add("/settings/proxy", function () {

View file

@ -33,9 +33,9 @@ bulmaswatch@^0.8.1:
integrity sha512-7HGm5v9If6gzxbTht4/oVS0dhySp6g/JyTrxmpSXHXgDQXivvxiuVmcJOZo3PFv9GAOn4om7SK36I2V8W81sgw==
"chokidar@>=3.0.0 <4.0.0":
version "3.5.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
@ -113,18 +113,18 @@ readdirp@~3.6.0:
picomatch "^2.2.1"
sass@^1.46.0:
version "1.48.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.48.0.tgz#b53cfccc1b8ab4be375cc54f306fda9d4711162c"
integrity sha512-hQi5g4DcfjcipotoHZ80l7GNJHGqQS5LwMBjVYB/TaT0vcSSpbgM8Ad7cgfsB2M0MinbkEQQPO9+sjjSiwxqmw==
version "1.49.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.49.0.tgz#65ec1b1d9a6bc1bae8d2c9d4b392c13f5d32c078"
integrity sha512-TVwVdNDj6p6b4QymJtNtRS2YtLJ/CqZriGg0eIAbAKMlN8Xy6kbv33FsEZSF7FufFFM705SQviHjjThfaQ4VNw==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
"source-map-js@>=0.6.2 <2.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
to-regex-range@^5.0.1:
version "5.0.1"

View file

@ -1,7 +1,7 @@
{layout '../layouts/default.latte'}
{block header}
<p class="title">About</p>
<p class="title">About</p>
{/block}
{block content}

View file

@ -1,9 +1,9 @@
{layout '../layouts/hero.latte'}
{block content}
<p class="title">There was an error processing your request!</p>
<p class="subtitle">HTTP Code: {$error->http_code}</p>
{if $error->tiktok_code}
<p class="subtitle">API error code {$error->tiktok_code} ({$error->tiktok_msg})</p>
{/if}
<p class="title">There was an error processing your request!</p>
<p class="subtitle">HTTP Code: {$error->http_code}</p>
{if $error->tiktok_code}
<p class="subtitle">API error code {$error->tiktok_code} ({$error->tiktok_msg})</p>
{/if}
{/block}

View file

@ -1,7 +1,7 @@
{layout '../layouts/default.latte'}
{block header}
<p class="title">Following</p>
<p class="title">Following</p>
{/block}
{block content}

View file

@ -1,58 +1,58 @@
{layout '../layouts/hero.latte'}
{block content}
<p class="title">Welcome to ProxiTok!</p>
<p class="subtitle">An alternative open source frontend for TikTok</p>
<p>Search user:</p>
<form id="username_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="username" class="input" type="text" placeholder="Type username" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Search video by id:</p>
<form id="video_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="video_id" class="input" type="text" placeholder="Type video ID" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Search tag:</p>
<form id="tag_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="tag" class="input" type="text" placeholder="Type tag" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Search music videos by id:</p>
<form id="music_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="music_id" class="input" type="text" placeholder="Type music id" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Trending:</p>
<a class="button is-success" href="./trending">Go</a>
<p class="title">Welcome to ProxiTok!</p>
<p class="subtitle">An alternative open source frontend for TikTok</p>
<p>Search user:</p>
<form id="username_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="username" class="input" type="text" placeholder="Type username" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Search video by id:</p>
<form id="video_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="video_id" class="input" type="text" placeholder="Type video ID" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Search tag:</p>
<form id="tag_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="tag" class="input" type="text" placeholder="Type tag" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Search music videos by id:</p>
<form id="music_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="music_id" class="input" type="text" placeholder="Type music id" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
</form>
<hr />
<p>Trending:</p>
<a class="button is-success" href="./trending">Go</a>
{/block}
{block extra}

View file

@ -0,0 +1,14 @@
<?php
namespace Views\Models;
/**
* Base for all templates, needs a Title to set
*/
class BaseTemplate {
public string $title;
public string $version;
function __construct(string $title) {
$this->title = $title;
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace Views\Models;
/**
* Base for templates with a feed
*/
class FeedTemplate extends BaseTemplate {
public object $feed;
function __construct(string $title, object $feed) {
parent::__construct($title);
$this->feed = $feed;
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace Views\Models;
/**
* Exclusive for /following
*/
class FollowingTemplate extends FeedTemplate {
public array $following;
function __construct(array $following, object $feed) {
parent::__construct('Following', $feed);
$this->following = $following;
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace Views\Models;
/**
* Base for templates with item (only /video at the time)
*/
class ItemTemplate extends BaseTemplate {
public object $item;
function __construct(string $title, object $item) {
parent::__construct($title);
$this->item = $item;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Views\Models;
use \Helpers\Following;
use \Helpers\Settings;
/**
* Exclusive for /settings
*/
class SettingsTemplate extends BaseTemplate {
public array $proxy_elements = [];
public array $following = [];
function __construct() {
parent::__construct('Settings');
$this->proxy_elements = Settings::PROXY;
$this->following = Following::get();
}
}

View file

@ -1,9 +1,9 @@
{layout '../layouts/default.latte'}
{block header}
<p class="title">{$feed->info->detail->music->title}</p>
<p class="subtitle">{$feed->info->detail->music->desc}</p>
<p>Videos: {number($feed->info->detail->stats->videoCount)}</p>
<p class="title">{$feed->info->detail->music->title}</p>
<p class="subtitle">{$feed->info->detail->music->desc}</p>
<p>Videos: {number($feed->info->detail->stats->videoCount)}</p>
{/block}
{block content}

View file

@ -1,15 +1,15 @@
{layout '../layouts/default.latte'}
{block header}
<p class="title">Settings</p>
<p class="title">Settings</p>
{/block}
{block content}
<!-- Proxy settings -->
<p class="title">Proxy</p>
{include '../components/settings/proxy.latte'}
<hr />
<!-- Following -->
<p class="title">Following</p>
{include '../components/settings/following.latte'}
<!-- Proxy settings -->
<p class="title">Proxy</p>
{include '../components/settings/proxy.latte'}
<hr />
<!-- Following -->
<p class="title">Following</p>
{include '../components/settings/following.latte'}
{/block}

View file

@ -1,9 +1,9 @@
{layout '../layouts/default.latte'}
{block header}
<p class="title">{$feed->info->detail->challenge->title}</p>
<p class="subtitle">{$feed->info->detail->challenge->desc}</p>
<p>Videos: {number($feed->info->detail->stats->videoCount)} / Views: {number($feed->info->detail->stats->viewCount)}</p>
<p class="title">{$feed->info->detail->challenge->title}</p>
<p class="subtitle">{$feed->info->detail->challenge->desc}</p>
<p>Videos: {number($feed->info->detail->stats->videoCount)} / Views: {number($feed->info->detail->stats->viewCount)}</p>
{/block}
{block content}

View file

@ -1,7 +1,7 @@
{layout '../layouts/default.latte'}
{block header}
<p class="title">Trending</p>
<p class="title">Trending</p>
{/block}
{block content}

View file

@ -1,13 +1,13 @@
{layout '../layouts/default.latte'}
{block header}
<figure class="figure is-96x96">
<img src="{path('stream?url=' . urlencode($feed->info->detail->user->avatarThumb))}" />
</figure>
<p class="title">{$feed->info->detail->user->uniqueId}'s profile</p>
<p class="subtitle">{$feed->info->detail->user->signature}</p>
<p>Following: {number($feed->info->detail->stats->followingCount)} / Followers: {number($feed->info->detail->stats->followerCount)}</p>
<p>Hearts: {number($feed->info->detail->stats->heartCount)} / Videos: {$feed->info->detail->stats->videoCount}</p>
<figure class="figure is-96x96">
<img src="{path('stream?url=' . urlencode($feed->info->detail->user->avatarThumb))}" />
</figure>
<p class="title">{$feed->info->detail->user->uniqueId}'s profile</p>
<p class="subtitle">{$feed->info->detail->user->signature}</p>
<p>Following: {number($feed->info->detail->stats->followingCount)} / Followers: {number($feed->info->detail->stats->followerCount)}</p>
<p>Hearts: {number($feed->info->detail->stats->heartCount)} / Videos: {$feed->info->detail->stats->videoCount}</p>
{/block}
{block content}

View file

@ -1,21 +1,21 @@
{layout '../layouts/hero.latte'}
{block content}
<div class="columns is-centered is-vcentered">
<div class="column">
<video autoplay controls src="{path('stream?url=' . urlencode($item->items[0]->video->playAddr))}"></video>
</div>
<div class="column has-text-centered">
<div class="box">
<p class="title">Video by <a href="{path('@'.$item->info->detail->user->uniqueId)}">{$item->info->detail->user->uniqueId}</a></p>
<p class="subtitle">{$item->items[0]->desc}</p>
<p>Played {number($item->info->detail->stats->playCount)} times</p>
<p>Shared {number($item->info->detail->stats->shareCount)} times / {number($item->info->detail->stats->commentCount)} comments</p>
<hr />
<a href="{path('stream?url=' . urlencode($item->items[0]->video->playAddr) . '&download=1')}" class="button is-info">Download video</a>
<p>{$item->items[0]->music->title}</p>
<audio src="{path('stream?url=' . urlencode($item->items[0]->music->playUrl))}" controls preload="none"></audio>
<div class="columns is-centered is-vcentered">
<div class="column">
<video autoplay controls src="{path('stream?url=' . urlencode($item->items[0]->video->playAddr))}"></video>
</div>
<div class="column has-text-centered">
<div class="box">
<p class="title">Video by <a href="{path('@'.$item->info->detail->user->uniqueId)}">{$item->info->detail->user->uniqueId}</a></p>
<p class="subtitle">{$item->items[0]->desc}</p>
<p>Played {number($item->info->detail->stats->playCount)} times</p>
<p>Shared {number($item->info->detail->stats->shareCount)} times / {number($item->info->detail->stats->commentCount)} comments</p>
<hr />
<a href="{path('stream?url=' . urlencode($item->items[0]->video->playAddr) . '&download=1')}" class="button is-info">Download video</a>
<p>{$item->items[0]->music->title}</p>
<audio src="{path('stream?url=' . urlencode($item->items[0]->music->playUrl))}" controls preload="none"></audio>
</div>
</div>
</div>
</div>
{/block}