mirror of
https://github.com/pablouser1/ProxiTok.git
synced 2024-12-06 19:27:30 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d273b35e53 | |||
| faf1dcee51 | |||
| 89d9ad2f9c | |||
| 7ddf6f7738 | |||
| 059bf7208a | |||
| d5858c39fd | |||
| 76be343c13 | |||
| a7b0edf8fe | |||
| 73e9d7a1cd | |||
| 0224cd25eb | |||
| 215f984fe4 | |||
| ee6e8b0593 | |||
| fa3f1e0a7c | |||
| ce5c9e016b |
@@ -3,7 +3,7 @@ name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: pablouser1
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.13.1'
|
||||
cosign-release: 'v2.0.0-rc.0'
|
||||
|
||||
|
||||
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||
|
||||
@@ -40,7 +40,8 @@ Apply to: Main window (address bar)
|
||||
* Add custom amount of videos per page
|
||||
|
||||
## Credits
|
||||
[@TheFrenchGhosty](https://github.com/TheFrenchGhosty): Initial Dockerfile and fixes to a usable state. You can check his Docker image [here](https://github.com/PussTheCat-org/docker-proxitok-quay) on Github or [here](https://quay.io/repository/pussthecatorg/proxitok) on Quay
|
||||
[TheFrenchGhosty](https://thefrenchghosty.me) ([Github](https://github.com/TheFrenchGhosty)): Initial Dockerfile and fixes to a usable state.
|
||||
|
||||
### External libraries
|
||||
* [TikScraperPHP](https://github.com/pablouser1/TikScraperPHP)
|
||||
* [Latte](https://github.com/nette/latte)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace App\Cache;
|
||||
|
||||
use TikScraper\Interfaces\CacheInterface;
|
||||
|
||||
class ApcuCache implements CacheInterface {
|
||||
function __construct() {
|
||||
if (!(extension_loaded('apcu') && apcu_enabled())) {
|
||||
throw new \Exception('APCu not enabled');
|
||||
}
|
||||
}
|
||||
|
||||
public function get(string $cache_key): ?object {
|
||||
$data = apcu_fetch($cache_key);
|
||||
return $data !== false ? json_decode($data) : null;
|
||||
}
|
||||
|
||||
public function exists(string $cache_key): bool {
|
||||
return apcu_exists($cache_key);
|
||||
}
|
||||
|
||||
public function set(string $cache_key, string $data, $timeout = 3600): void {
|
||||
apcu_store($cache_key, $data, $timeout);
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,6 @@ namespace App\Constants;
|
||||
|
||||
abstract class CacheMethods {
|
||||
const JSON = 'json';
|
||||
const APCU = 'apcu';
|
||||
const REDIS = 'redis';
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ class EmbedController {
|
||||
$video = $api->video($id);
|
||||
$video->feed();
|
||||
if ($video->ok()) {
|
||||
$data = $video->getFull();
|
||||
Wrappers::latte('video', new VideoTemplate($data->feed->items[0], $data->info->detail, true));
|
||||
$item = $video->getFeed()->items[0];
|
||||
$info = $video->getInfo();
|
||||
Wrappers::latte('video', new VideoTemplate($item, $info, true));
|
||||
} else {
|
||||
ErrorHandler::showMeta($video->error());
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ class MusicController {
|
||||
$music = $api->music($music_id);
|
||||
$music->feed($cursor);
|
||||
if ($music->ok()) {
|
||||
$data = $music->getFull();
|
||||
Wrappers::latte('music', new FullTemplate('Music', $data));
|
||||
$info = $music->getInfo();
|
||||
$feed = $music->getFeed();
|
||||
Wrappers::latte('music', new FullTemplate('Music', $info, $feed));
|
||||
} else {
|
||||
ErrorHandler::showMeta($music->error());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Helpers\Cookies;
|
||||
use TikScraper\Helpers\Converter;
|
||||
|
||||
class ProxyController {
|
||||
const VALID_TIKTOK_DOMAINS = [
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
namespace App\Controllers;
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Helpers\UrlBuilder;
|
||||
|
||||
/**
|
||||
* Used to be compatible with HTML forms
|
||||
*/
|
||||
class RedirectController {
|
||||
static public function redirect() {
|
||||
static public function search() {
|
||||
$endpoint = '/';
|
||||
if (isset($_GET['type'], $_GET['term'])) {
|
||||
$term = trim($_GET['term']);
|
||||
@@ -48,6 +49,24 @@ class RedirectController {
|
||||
header("Location: {$url}");
|
||||
}
|
||||
|
||||
static public function download() {
|
||||
if (!(isset($_GET['videoId'], $_GET['authorUsername'], $_GET['playAddr']))) {
|
||||
ErrorHandler::showText(400, 'Request incomplete');
|
||||
return;
|
||||
}
|
||||
|
||||
$watermark = isset($_GET['watermark']) && $_GET['watermark'] === 'yes' ? true : false;
|
||||
|
||||
$url = '';
|
||||
if ($watermark) {
|
||||
$url = UrlBuilder::download($_GET['playAddr'], $_GET['authorUsername'], $_GET['videoId'], true);
|
||||
} else {
|
||||
$url = UrlBuilder::download(UrlBuilder::video_external($_GET['authorUsername'], $_GET['videoId']), $_GET['authorUsername'], $_GET['videoId'], false);
|
||||
}
|
||||
|
||||
header("Location: {$url}");
|
||||
}
|
||||
|
||||
/**
|
||||
* to_endpoint maps a TikTok URL into a ProxiTok-compatible endpoint URL.
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,9 @@ class TagController {
|
||||
$hashtag = $api->hashtag($name);
|
||||
$hashtag->feed($cursor);
|
||||
if ($hashtag->ok()) {
|
||||
$data = $hashtag->getFull();
|
||||
Wrappers::latte('tag', new FullTemplate($data->info->detail->title, $data));
|
||||
$info = $hashtag->getInfo();
|
||||
$feed = $hashtag->getFeed();
|
||||
Wrappers::latte('tag', new FullTemplate($info->detail->title, $info, $feed));
|
||||
} else {
|
||||
ErrorHandler::showMeta($hashtag->error());
|
||||
}
|
||||
|
||||
@@ -16,12 +16,13 @@ class UserController {
|
||||
$user = $api->user($username);
|
||||
$user->feed($cursor);
|
||||
if ($user->ok()) {
|
||||
$data = $user->getFull();
|
||||
if ($data->info->detail->privateAccount) {
|
||||
$info = $user->getInfo();
|
||||
$feed = $user->getFeed();
|
||||
if ($info->detail->privateAccount) {
|
||||
ErrorHandler::showText(401, "Private account detected! Not supported");
|
||||
return;
|
||||
}
|
||||
Wrappers::latte('user', new FullTemplate($data->info->detail->nickname, $data));
|
||||
Wrappers::latte('user', new FullTemplate($info->detail->nickname, $info, $feed));
|
||||
} else {
|
||||
ErrorHandler::showMeta($user->error());
|
||||
}
|
||||
@@ -32,8 +33,9 @@ class UserController {
|
||||
$video = $api->video($video_id);
|
||||
$video->feed();
|
||||
if ($video->ok()) {
|
||||
$data = $video->getFull();
|
||||
Wrappers::latte('video', new VideoTemplate($data->feed->items[0], $data->info->detail));
|
||||
$item = $video->getFeed()->items[0];
|
||||
$info = $video->getInfo();
|
||||
Wrappers::latte('video', new VideoTemplate($item, $info));
|
||||
} else {
|
||||
ErrorHandler::showMeta($video->error());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Cache\ApcuCache;
|
||||
use App\Cache\JSONCache;
|
||||
use App\Cache\RedisCache;
|
||||
use App\Constants\CacheMethods;
|
||||
@@ -76,6 +77,9 @@ class Wrappers {
|
||||
$latte->addFunction('url_user', function (string $username): string {
|
||||
return UrlBuilder::user($username);
|
||||
});
|
||||
$latte->addFunction('url_tag', function (string $tag): string {
|
||||
return UrlBuilder::tag($tag);
|
||||
});
|
||||
$latte->addFunction('url_video_internal', function (string $username, string $id): string {
|
||||
return UrlBuilder::video_internal($username, $id);
|
||||
});
|
||||
@@ -135,6 +139,9 @@ class Wrappers {
|
||||
case CacheMethods::JSON:
|
||||
$cacheEngine = new JSONCache();
|
||||
break;
|
||||
case CacheMethods::APCU:
|
||||
$cacheEngine = new ApcuCache();
|
||||
break;
|
||||
case CacheMethods::REDIS:
|
||||
if (!(isset($_ENV['REDIS_URL']) || isset($_ENV['REDIS_HOST'], $_ENV['REDIS_PORT']))) {
|
||||
throw new \Exception('You need to set REDIS_URL or REDIS_HOST and REDIS_PORT to use Redis Cache!');
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use TikScraper\Models\Full;
|
||||
use TikScraper\Models\Feed;
|
||||
use TikScraper\Models\Info;
|
||||
|
||||
/**
|
||||
* Base for templates with both info and feed
|
||||
*/
|
||||
class FullTemplate extends BaseTemplate {
|
||||
public Full $data;
|
||||
public Info $info;
|
||||
public Feed $feed;
|
||||
|
||||
function __construct(string $title, Full $data) {
|
||||
function __construct(string $title, Info $info, Feed $feed) {
|
||||
parent::__construct($title);
|
||||
$this->data = $data;
|
||||
$this->info = $info;
|
||||
$this->feed = $feed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use TikScraper\Models\Info;
|
||||
|
||||
/**
|
||||
* Base for templates with a feed
|
||||
*/
|
||||
class VideoTemplate extends BaseTemplate {
|
||||
public object $item;
|
||||
public object $detail;
|
||||
public Info $info;
|
||||
public string $layout = 'hero';
|
||||
|
||||
function __construct(object $item, object $detail, bool $isEmbed = false) {
|
||||
function __construct(object $item, Info $info, bool $isEmbed = false) {
|
||||
parent::__construct('Video');
|
||||
$this->item = $item;
|
||||
$this->detail = $detail;
|
||||
$this->info = $info;
|
||||
if ($isEmbed) {
|
||||
$this->layout = 'embed';
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "pablouser1/proxitok",
|
||||
"description": "An alternative frontend for TikTok",
|
||||
"version": "2.4.3.2",
|
||||
"version": "2.4.4.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"type": "project",
|
||||
"authors": [
|
||||
|
||||
Generated
+13
-13
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "79c77d98109f2c4346e1d4c8523b7be0",
|
||||
"content-hash": "c6c6c5ecb586c3f4701ffb57ccf385e4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bramus/router",
|
||||
@@ -263,16 +263,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pablouser1/tikscraper",
|
||||
"version": "v2.3.2.2",
|
||||
"version": "v2.3.3.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pablouser1/TikScraperPHP.git",
|
||||
"reference": "2016443571f87265ca8e37897d88009affdcd0b6"
|
||||
"reference": "065cd740373f6f423d366d73ea94437041ec85cd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pablouser1/TikScraperPHP/zipball/2016443571f87265ca8e37897d88009affdcd0b6",
|
||||
"reference": "2016443571f87265ca8e37897d88009affdcd0b6",
|
||||
"url": "https://api.github.com/repos/pablouser1/TikScraperPHP/zipball/065cd740373f6f423d366d73ea94437041ec85cd",
|
||||
"reference": "065cd740373f6f423d366d73ea94437041ec85cd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -305,9 +305,9 @@
|
||||
"description": "Get data from TikTok API",
|
||||
"support": {
|
||||
"issues": "https://github.com/pablouser1/TikScraperPHP/issues",
|
||||
"source": "https://github.com/pablouser1/TikScraperPHP/tree/v2.3.2.2"
|
||||
"source": "https://github.com/pablouser1/TikScraperPHP/tree/v2.3.3.4"
|
||||
},
|
||||
"time": "2022-11-22T19:43:57+00:00"
|
||||
"time": "2023-01-25T14:17:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-webdriver/webdriver",
|
||||
@@ -572,16 +572,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v5.4.11",
|
||||
"version": "v5.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1"
|
||||
"reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1",
|
||||
"reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/c5ba874c9b636dbccf761e22ce750e88ec3f55e1",
|
||||
"reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -614,7 +614,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.11"
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -630,7 +630,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-06-27T16:58:25+00:00"
|
||||
"time": "2023-01-01T08:32:19+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
||||
Vendored
+1
@@ -28,6 +28,7 @@ $bulmaswatch-import-font: false;
|
||||
@import "./node_modules/bulma/sass/form/tools.sass";
|
||||
|
||||
// Components
|
||||
@import "./node_modules/bulma/sass/components/breadcrumb.sass";
|
||||
@import "./node_modules/bulma/sass/components/card.sass";
|
||||
@import "./node_modules/bulma/sass/components/dropdown.sass";
|
||||
@import "./node_modules/bulma/sass/components/media.sass";
|
||||
|
||||
+32
-1
@@ -2,6 +2,7 @@
|
||||
/** @var \Bramus\Router\Router $router */
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\BaseTemplate;
|
||||
|
||||
@@ -21,9 +22,39 @@ $router->get('/verify', function () {
|
||||
Wrappers::latte('verify', new BaseTemplate('verify'));
|
||||
});
|
||||
|
||||
$router->get('/manifest', function () {
|
||||
header('Content-Type: application/json');
|
||||
$data = [
|
||||
"name" => "ProxiTok",
|
||||
"short_name" => "ProxiTok",
|
||||
"description" => "Use TikTok with a privacy-friendly alternative frontend",
|
||||
"lang" => "en-US",
|
||||
"theme_color" => "#4040ff",
|
||||
"background_color" => "#ffffff",
|
||||
"display" => "standalone",
|
||||
"orientation" => "portrait-primary",
|
||||
"icons" => [
|
||||
[
|
||||
"src" => Misc::url('/android-chrome-192x192.png'),
|
||||
"sizes" => "192x192",
|
||||
"type" => "image/png"
|
||||
],
|
||||
[
|
||||
"src" => Misc::url('/android-chrome-512x512.png'),
|
||||
"sizes" => "512x512",
|
||||
"type" => "image/png"
|
||||
]
|
||||
],
|
||||
"start_url" => Misc::url('/'),
|
||||
"scope" => Misc::url('/')
|
||||
];
|
||||
echo json_encode($data, JSON_PRETTY_PRINT);
|
||||
});
|
||||
|
||||
$router->get('/stream', 'ProxyController@stream');
|
||||
$router->get('/download', 'ProxyController@download');
|
||||
$router->get('/redirect', 'RedirectController@redirect');
|
||||
$router->get('/redirect/search', 'RedirectController@search');
|
||||
$router->get('/redirect/download', 'RedirectController@download');
|
||||
|
||||
$router->mount('/trending', function () use ($router) {
|
||||
$router->get('/', 'TrendingController@get');
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"name": "ProxiTok",
|
||||
"short_name": "ProxiTok",
|
||||
"description": "Use TikTok with a privacy-friendly alternative frontend",
|
||||
"orientation": "portrait-primary",
|
||||
"icons": [
|
||||
{
|
||||
"src": "android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"id": "./",
|
||||
"start_url": "./",
|
||||
"theme_color": "#4040ff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -5,11 +5,27 @@
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{path('/apple-touch-icon.png')}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{path('/favicon-32x32.png')}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{path('/favicon-16x16.png')}">
|
||||
<link rel="manifest" href="{path('/site.webmanifest')}">
|
||||
<link rel="manifest" href="{path('/manifest')}">
|
||||
{if isset($og, $og_content, $og_url)}
|
||||
<!-- Using TikTok's meta config -->
|
||||
<meta property="og:title" content="{$og->title}" />
|
||||
<meta property="og:description" content="{$og->description}" />
|
||||
<meta property="og:url" content="{$og_url}" />
|
||||
<meta property="og:image" content="{url_stream($og_content)}" />
|
||||
<meta property="twitter:title" content="{$og->title}" />
|
||||
<meta property="twitter:description" content="{$og->description}" />
|
||||
<meta property="twitter:image" content="{url_stream($og_content)}" />
|
||||
{else}
|
||||
<!-- Using predifined ProxiTok config -->
|
||||
<meta property="og:title" content="{$title}" />
|
||||
<meta property="og:description" content="Alternative frontend for TikTok" />
|
||||
<meta property="twitter:title" content="{$title}" />
|
||||
<meta property="twitter:description" content="Alternative frontend for TikTok" />
|
||||
{/if}
|
||||
<meta property="og:site_name" content="ProxiTok" />
|
||||
<meta property="og:title" content="{$title}" />
|
||||
<meta property="og:description" content="Alternative frontend for TikTok" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="twitter:card" content="summary" />
|
||||
<meta property="twitter:site" content="ProxiTok" />
|
||||
{if isset($has_rss)}
|
||||
<link rel="alternate" type="application/rss+xml" title="{$title}" href="{$_SERVER['REQUEST_URI'] . '/rss'}" />
|
||||
{/if}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<link rel="stylesheet" href="{static('css', 'themes/card.css')}">
|
||||
<noscript>JavaScript is required for this section to work!</noscript>
|
||||
<section class="section">
|
||||
<noscript>JavaScript is required for this section to work!</noscript>
|
||||
<div class="columns is-multiline is-vcentered">
|
||||
{foreach $data->feed->items as $item}
|
||||
{foreach $feed->items as $item}
|
||||
{do $share_url = url_video_external($item->author->uniqueId, $item->id)}
|
||||
<div class="column is-one-quarter clickable-img" id="{$item->id}" onclick="openVideo(this.id)"
|
||||
data-video_url="{url_stream($item->video->playAddr)}"
|
||||
@@ -17,7 +17,7 @@
|
||||
<img class="hidden" loading="lazy" data-src="{url_stream($item->video->dynamicCover)}" />
|
||||
</div>
|
||||
{/foreach}
|
||||
{if empty($data->feed->items)}
|
||||
{if empty($feed->items)}
|
||||
<p class="title">No items sent by TikTok!</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div n:ifset="$data->info" class="buttons">
|
||||
<div n:ifset="$info" class="buttons">
|
||||
{* is_numeric is used to avoid having a back button with ttwid cursors *}
|
||||
{if isset($_GET['cursor']) && is_numeric($_GET['cursor']) && $_GET['cursor'] != 0}
|
||||
<a class="button is-danger" href="?cursor=0">First</a>
|
||||
{/if}
|
||||
<a n:attr="disabled => !$data->feed->hasMore" class="button is-success" href="?cursor={$data->feed->maxCursor}">Next</a>
|
||||
<a n:attr="disabled => !$feed->hasMore" class="button is-success" href="?cursor={$feed->maxCursor}">Next</a>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
<div class="dropdown is-hoverable">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button is-success" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||
{include '../../icon.latte', icon: 'software-download', text: 'Download'}
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a target="_blank" href="{url_download($playAddr, $uniqueId, $id, true)}" class="dropdown-item">Watermark</a>
|
||||
<a target="_blank" href="{url_download(url_video_external($uniqueId, $id), $uniqueId, $id, false)}" class="dropdown-item">No watermark</a>
|
||||
{embed '../../form.latte', path: '/redirect/download', method: 'GET'}
|
||||
{block fields}
|
||||
<div class="field has-addons has-addons-centered">
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="watermark">
|
||||
<option value="yes">WM</option>
|
||||
<option value="no">No WM</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-success">
|
||||
{include '../../icon.latte', icon: 'software-download', text: 'Download'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="playAddr" value="{$playAddr}" />
|
||||
<input type="hidden" name="videoId" value="{$id}" />
|
||||
<input type="hidden" name="authorUsername" value="{$uniqueId}" />
|
||||
{/block}
|
||||
{/embed}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
<div class="dropdown is-hoverable">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button is-primary" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||
{include '../../icon.latte', icon: 'share', text: 'Share'}
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a href="{url_video_internal($uniqueId, $id)}" class="dropdown-item has-text-success">
|
||||
<nav class="breadcrumb is-centered" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<a class="has-text-success" href="{url_video_internal($uniqueId, $id)}">
|
||||
{include '../../icon.latte', icon: 'lock', text: 'Instance link'}
|
||||
</a>
|
||||
<a href="{url_video_external($uniqueId, $id)}" class="dropdown-item has-text-warning">
|
||||
</li>
|
||||
<li>
|
||||
<a class="has-text-warning" href="{url_video_external($uniqueId, $id)}">
|
||||
{include '../../icon.latte', icon: 'lock-unlock', text: 'Original Link'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="container">
|
||||
{foreach $data->feed->items as $item}
|
||||
{foreach $feed->items as $item}
|
||||
<article class="media">
|
||||
<figure class="media-left">
|
||||
<p class="image is-64x64">
|
||||
<img src="{url_stream($item->author->avatarThumb)}" />
|
||||
<img class="is-rounded" src="{url_stream($item->author->avatarThumb)}" />
|
||||
</p>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
@@ -25,13 +25,13 @@
|
||||
</video>
|
||||
</div>
|
||||
<div class="has-text-centered">
|
||||
{include './common/share.latte', uniqueId: $item->author->uniqueId, id: $item->id}
|
||||
{include './common/download.latte', playAddr: $item->video->playAddr, id: $item->id, uniqueId: $item->author->uniqueId}
|
||||
<div class="mt-2">{include './common/share.latte', uniqueId: $item->author->uniqueId, id: $item->id}</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{/foreach}
|
||||
{if empty($data->feed->items)}
|
||||
{if empty($feed->items)}
|
||||
<p class="title">No items sent by TikTok!</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{block content}
|
||||
<p class="title has-text-centered">Welcome to ProxiTok!</p>
|
||||
<p class="subtitle has-text-centered">An alternative open source frontend for TikTok</p>
|
||||
{embed '../components/form.latte', path: '/redirect', method: 'GET'}
|
||||
{embed '../components/form.latte', path: '/redirect/search', method: 'GET'}
|
||||
{block fields}
|
||||
<div class="field has-addons has-addons-centered">
|
||||
<div class="control">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{layout '../layouts/default.latte'}
|
||||
|
||||
{block header}
|
||||
<p class="title">{$data->info->detail->title}</p>
|
||||
<p class="subtitle">{$data->info->detail->desc}</p>
|
||||
<p>Videos: {number($data->info->stats->videoCount)}</p>
|
||||
<p class="title">{$info->detail->title}</p>
|
||||
<p class="subtitle">{$info->detail->desc}</p>
|
||||
<p>Videos: {number($info->stats->videoCount)}</p>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
|
||||
{var $has_rss = true}
|
||||
|
||||
{var $og = $info->meta->og}
|
||||
{var $og_content = $info->detail->profileLarger}
|
||||
{var $og_url = url_tag($info->detail->title)}
|
||||
|
||||
{block header}
|
||||
{if $data->info->detail->profileThumb !== ''}
|
||||
<figure class="figure is-96x96">
|
||||
<img src="{url_stream($data->info->detail->profileThumb)}" />
|
||||
{if $info->detail->profileLarger !== ''}
|
||||
<figure class="image is-inline-block is-128x128">
|
||||
<img class="is-rounded" src="{url_stream($info->detail->profileLarger)}" />
|
||||
</figure>
|
||||
{/if}
|
||||
<p class="title">{$data->info->detail->title}</p>
|
||||
<p class="title">{$info->detail->title}</p>
|
||||
<p class="subtitle">{include '../components/rss.latte'}</p>
|
||||
<p>Videos: {number($data->info->stats->videoCount)} / Views: {number($data->info->stats->viewCount)}</p>
|
||||
<p>Videos: {number($info->stats->videoCount)} / Views: {number($info->stats->viewCount)}</p>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
|
||||
{var $has_rss = true}
|
||||
|
||||
{var $og = $info->meta->og}
|
||||
{var $og_content = $info->detail->avatarLarger}
|
||||
{var $og_url = url_user($info->detail->uniqueId)}
|
||||
|
||||
{block header}
|
||||
<figure class="figure is-96x96">
|
||||
<img src="{url_stream($data->info->detail->avatarThumb)}" />
|
||||
<figure class="image is-inline-block is-128x128">
|
||||
<img class="is-rounded" src="{url_stream($info->detail->avatarLarger)}" />
|
||||
</figure>
|
||||
<p class="title">{$data->info->detail->uniqueId}</p>
|
||||
<p class="title">{$info->detail->uniqueId}</p>
|
||||
<p class="subtitle">{include '../components/rss.latte'}</p>
|
||||
<p>{$data->info->detail->signature}</p>
|
||||
<p>Following: {number($data->info->stats->followingCount)} / Followers: {number($data->info->stats->followerCount)}</p>
|
||||
<p>Hearts: {number($data->info->stats->heartCount)} / Videos: {$data->info->stats->videoCount}</p>
|
||||
<p>{$info->detail->signature}</p>
|
||||
<p>Following: {number($info->stats->followingCount)} / Followers: {number($info->stats->followerCount)}</p>
|
||||
<p>Hearts: {number($info->stats->heartCount)} / Videos: {$info->stats->videoCount}</p>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{layout "../layouts/{$layout}.latte"}
|
||||
|
||||
{var $og = $info->meta->og}
|
||||
{var $og_content = $item->video->originCover}
|
||||
{var $og_url = url_video_internal($info->detail->uniqueId, $item->id)}
|
||||
|
||||
{block content}
|
||||
<div class="columns is-centered is-vcentered is-gapless">
|
||||
<div class="column has-text-centered">
|
||||
@@ -12,14 +16,14 @@
|
||||
<article class="media">
|
||||
<figure class="media-left">
|
||||
<p class="image is-64x64">
|
||||
<img src="{url_stream($detail->avatarThumb)}" />
|
||||
<img src="{url_stream($info->detail->avatarThumb)}" />
|
||||
</p>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<p>
|
||||
<strong>{$detail->nickname}</strong>
|
||||
<strong>{$info->detail->nickname}</strong>
|
||||
<small>
|
||||
<a href="{url_user($detail->uniqueId)}">@{$detail->uniqueId}</a>
|
||||
<a href="{url_user($info->detail->uniqueId)}">@{$info->detail->uniqueId}</a>
|
||||
</small>
|
||||
<small title="{date('M d, Y H:i:s e', $item->createTime)}">{date('M d, Y', $item->createTime)}</small>
|
||||
</p>
|
||||
@@ -30,8 +34,8 @@
|
||||
<p n:ifcontent>{$item->desc}</p>
|
||||
{include '../components/themes/common/stats.latte', playCount: $item->stats->playCount, diggCount: $item->stats->diggCount, commentCount: $item->stats->commentCount, shareCount: $item->stats->shareCount}
|
||||
<div class="has-text-centered">
|
||||
{include '../components/themes/common/share.latte', uniqueId: $detail->uniqueId, id: $item->id}
|
||||
{include '../components/themes/common/download.latte', playAddr: $item->video->playAddr, id: $item->id, uniqueId: $detail->uniqueId}
|
||||
{include '../components/themes/common/share.latte', uniqueId: $info->detail->uniqueId, id: $item->id}
|
||||
{include '../components/themes/common/download.latte', playAddr: $item->video->playAddr, id: $item->id, uniqueId: $info->detail->uniqueId}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user