mirror of
https://github.com/pablouser1/ProxiTok.git
synced 2024-12-06 19:27:30 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3ebb08d26 | |||
| 19e2c32c77 | |||
| 0c4616c695 | |||
| fccec753d9 | |||
| 7203bc1509 | |||
| d871ed4e68 |
+2
-2
@@ -1,9 +1,9 @@
|
||||
# APP_URL="http://localhost:8000" # Full url path, PLEASE REPLACE TO YOUR OWN ONE
|
||||
|
||||
# SIGNER_URL="https://tiktok-sign.herokuapp.com/signature" # External signing service
|
||||
# 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 cache, used on Helpers\CacheEngines\RedisCache (CHOOSE ONE)
|
||||
# Redis cache, used on Helpers\CacheEngines\RedisCache
|
||||
# REDIS_HOST=localhost # Host or path to unix socket
|
||||
# REDIS_PORT=6379 # Set to -1 to use unix socket
|
||||
# REDIS_PASSWORD=example # Leave commented for no password
|
||||
|
||||
@@ -60,7 +60,7 @@ location /proxitok/.env {
|
||||
* i18n
|
||||
|
||||
## Credits
|
||||
* [TikTok-API-PHP](https://github.com/ssovit/TikTok-API-PHP) (Currently using my personal fork)
|
||||
* [TikScraper](https://github.com/pablouser1/TikScraperPHP)
|
||||
* [Latte](https://github.com/nette/latte)
|
||||
* [bramus/router](https://github.com/bramus/router)
|
||||
* [PHP dotenv](https://github.com/vlucas/phpdotenv)
|
||||
|
||||
+21
-23
@@ -1,33 +1,31 @@
|
||||
<?php
|
||||
namespace App\Cache;
|
||||
|
||||
use App\Helpers\Misc;
|
||||
|
||||
class JSONCache {
|
||||
private string $cache_path = __DIR__ . '/../../cache/api';
|
||||
private string $cache_path = '';
|
||||
|
||||
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 {
|
||||
$filename = $this->cache_path . '/' . $cache_key . '.json';
|
||||
if (is_file($filename)) {
|
||||
$time = time();
|
||||
$json_string = file_get_contents($filename);
|
||||
$element = json_decode($json_string);
|
||||
if ($time < $element->expires) {
|
||||
return $element->data;
|
||||
}
|
||||
// Remove file if expired
|
||||
unlink($filename);
|
||||
}
|
||||
return false;
|
||||
$this->cache_path = Misc::env('API_CACHE_JSON', __DIR__ . '/../../cache/api');
|
||||
}
|
||||
|
||||
public function set(string $cache_key, mixed $data, $timeout = 3600) {
|
||||
file_put_contents($this->cache_path . '/' . $cache_key . '.json', json_encode([
|
||||
'data' => $data,
|
||||
'expires' => time() + $timeout
|
||||
]));
|
||||
public function get(string $cache_key): ?object {
|
||||
$filename = $this->cache_path . '/' . $cache_key . '.json';
|
||||
if (is_file($filename)) {
|
||||
$json_string = file_get_contents($filename);
|
||||
$element = json_decode($json_string);
|
||||
return $element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function exists(string $cache_key): bool {
|
||||
$filename = $this->cache_path . '/' . $cache_key . '.json';
|
||||
return is_file($filename);
|
||||
}
|
||||
|
||||
public function set(string $cache_key, string $data, $timeout = 3600) {
|
||||
file_put_contents($this->cache_path . '/' . $cache_key . '.json', $data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ class RedisCache {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function exists(string $cache_key): bool {
|
||||
return $this->client->exists($cache_key);
|
||||
}
|
||||
|
||||
public function set(string $cache_key, array $data, $timeout = 3600) {
|
||||
$this->client->set($cache_key, json_encode($data), $timeout);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Models\FeedTemplate;
|
||||
use App\Models\DiscoverTemplate;
|
||||
|
||||
class DiscoverController {
|
||||
static public function get() {
|
||||
@@ -11,7 +11,7 @@ class DiscoverController {
|
||||
$feed = $api->getDiscover();
|
||||
if ($feed->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('discover'), new FeedTemplate('Discover', $feed));
|
||||
$latte->render(Misc::getView('discover'), new DiscoverTemplate($feed));
|
||||
} else {
|
||||
ErrorHandler::show($feed->meta);
|
||||
}
|
||||
|
||||
@@ -19,29 +19,45 @@ class ProxyController {
|
||||
return false;
|
||||
}
|
||||
|
||||
static public function stream() {
|
||||
static private function checkUrl() {
|
||||
if (!isset($_GET['url'])) {
|
||||
die('You need to send a url!');
|
||||
die('You need to send a URL');
|
||||
}
|
||||
|
||||
$url = $_GET['url'];
|
||||
if (!filter_var($url, FILTER_VALIDATE_URL) || !self::isValidDomain($url)) {
|
||||
if (!filter_var($_GET['url'], FILTER_VALIDATE_URL) || !self::isValidDomain($_GET['url'])) {
|
||||
die('Not a valid URL');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static private function getFileName(): string {
|
||||
$filename = 'tiktok-video';
|
||||
if (isset($_GET['user'])) {
|
||||
$filename .= '-' . $_GET['user'] . '-' . $_GET['id'];
|
||||
}
|
||||
return $filename;
|
||||
}
|
||||
|
||||
static public function stream() {
|
||||
if (isset($_GET['download'])) {
|
||||
// Download
|
||||
$downloader = new \Sovit\TikTok\Download();
|
||||
$filename = 'tiktok-video';
|
||||
if (isset($_GET['id'], $_GET['user'])) {
|
||||
$filename .= '-' . $_GET['user'] . '-' . $_GET['id'];
|
||||
}
|
||||
$downloader = new \TikScraper\Download();
|
||||
$watermark = isset($_GET['watermark']);
|
||||
$downloader->url($url, $filename, $watermark);
|
||||
if ($watermark) {
|
||||
self::checkUrl();
|
||||
$filename = self::getFileName();
|
||||
$downloader->url($_GET['url'], $filename, true);
|
||||
} else {
|
||||
if (!isset($_GET['id'])) {
|
||||
die('You need to send an ID!');
|
||||
}
|
||||
$filename = self::getFileName();
|
||||
$downloader->url($_GET['id'], $filename, false);
|
||||
}
|
||||
} else {
|
||||
// Stream
|
||||
$streamer = new \Sovit\TikTok\Stream();
|
||||
$streamer->stream($url);
|
||||
self::checkUrl();
|
||||
$url = $_GET['url'];
|
||||
$streamer = new \TikScraper\Stream();
|
||||
$streamer->url($url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class TagController {
|
||||
static public function get(string $name) {
|
||||
$cursor = Misc::getCursor();
|
||||
$api = Misc::api();
|
||||
$feed = $api->getChallengeFeed($name, $cursor);
|
||||
$feed = $api->getHashtagFeed($name, $cursor);
|
||||
if ($feed->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('tag'), new FeedTemplate('Tag', $feed));
|
||||
@@ -21,9 +21,9 @@ class TagController {
|
||||
|
||||
static public function rss(string $name) {
|
||||
$api = Misc::api();
|
||||
$feed = $api->getChallengeFeed($name);
|
||||
$feed = $api->getHashtagFeed($name);
|
||||
if ($feed->meta->success) {
|
||||
$feed = RSS::build("/tag/{$name}", "{$name} Tag", $feed->info->detail->challenge->desc, $feed->items);
|
||||
$feed = RSS::build("/tag/{$name}", "{$name} Tag", $feed->info->detail->desc, $feed->items);
|
||||
// Setup headers
|
||||
RSS::setHeaders('tag.rss');
|
||||
echo $feed;
|
||||
|
||||
@@ -9,8 +9,9 @@ use App\Helpers\RSS;
|
||||
class TrendingController {
|
||||
static public function get() {
|
||||
$cursor = Misc::getCursor();
|
||||
$page = $_GET['page'] ?? 0;
|
||||
$api = Misc::api();
|
||||
$feed = $api->getTrendingFeed($cursor);
|
||||
$feed = $api->getTrending($cursor, $page);
|
||||
if ($feed->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('trending'), new FeedTemplate('Trending', $feed));
|
||||
@@ -21,7 +22,7 @@ class TrendingController {
|
||||
|
||||
static public function rss() {
|
||||
$api = Misc::api();
|
||||
$feed = $api->getTrendingFeed();
|
||||
$feed = $api->getTrending();
|
||||
if ($feed->meta->success) {
|
||||
$feed = RSS::build('/trending', 'Trending', 'Tiktok trending', $feed->items);
|
||||
// Setup headers
|
||||
|
||||
@@ -12,12 +12,12 @@ class UserController {
|
||||
$api = Misc::api();
|
||||
$feed = $api->getUserFeed($username, $cursor);
|
||||
if ($feed->meta->success) {
|
||||
if ($feed->info->detail->user->privateAccount) {
|
||||
if ($feed->info->detail->privateAccount) {
|
||||
http_response_code(400);
|
||||
echo 'Private account detected! Not supported';
|
||||
}
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('user'), new FeedTemplate($feed->info->detail->user->nickname, $feed));
|
||||
$latte->render(Misc::getView('user'), new FeedTemplate($feed->info->detail->nickname, $feed));
|
||||
} else {
|
||||
ErrorHandler::show($feed->meta);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class UserController {
|
||||
$api = Misc::api();
|
||||
$feed = $api->getUserFeed($username);
|
||||
if ($feed->meta->success) {
|
||||
$feed = RSS::build('/@'.$username, $feed->info->detail->user->nickname, $feed->info->detail->user->signature, $feed->items);
|
||||
$feed = RSS::build('/@'.$username, $feed->info->detail->nickname, $feed->info->detail->signature, $feed->items);
|
||||
// Setup headers
|
||||
RSS::setHeaders('user.rss');
|
||||
echo $feed;
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Models\ItemTemplate;
|
||||
use App\Models\FeedTemplate;
|
||||
|
||||
class VideoController {
|
||||
static public function get(string $video_id) {
|
||||
@@ -11,7 +11,7 @@ class VideoController {
|
||||
$item = $api->getVideoByID($video_id);
|
||||
if ($item->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('video'), new ItemTemplate($item->info->detail->user->nickname, $item));
|
||||
$latte->render(Misc::getView('video'), new FeedTemplate($item->info->detail->nickname, $item));
|
||||
} else {
|
||||
ErrorHandler::show($item->meta);
|
||||
}
|
||||
|
||||
@@ -27,8 +27,10 @@ class Misc {
|
||||
/**
|
||||
* Setup of TikTok Api wrapper
|
||||
*/
|
||||
static public function api(): \Sovit\TikTok\Api {
|
||||
$options = [];
|
||||
static public function api(): \TikScraper\Api {
|
||||
$options = [
|
||||
'remote_signer' => self::env('SIGNER_URL', 'http://localhost:8080/signature')
|
||||
];
|
||||
$cacheEngine = false;
|
||||
// Proxy config
|
||||
foreach(Cookies::PROXY as $proxy_element) {
|
||||
@@ -55,13 +57,13 @@ class Misc {
|
||||
} else {
|
||||
$host = $_ENV['REDIS_HOST'];
|
||||
$port = (int) $_ENV['REDIS_PORT'];
|
||||
$password = isset($_ENV['REDIS_PASSWORD']) ? $_ENV['REDIS_PASSWORD'] : null;
|
||||
$password = $_ENV['REDIS_PASSWORD'] ?? null;
|
||||
}
|
||||
$cacheEngine = new RedisCache($host, $port, $password);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$api = new \Sovit\TikTok\Api($options, $cacheEngine);
|
||||
$api = new \TikScraper\Api($options, $cacheEngine);
|
||||
return $api;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use TikScraper\Models\Discover;
|
||||
|
||||
/**
|
||||
* Base for templates with a feed
|
||||
*/
|
||||
class DiscoverTemplate extends BaseTemplate {
|
||||
public Discover $feed;
|
||||
|
||||
function __construct(Discover $feed) {
|
||||
parent::__construct('Discover');
|
||||
$this->feed = $feed;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use TikScraper\Models\Feed;
|
||||
|
||||
/**
|
||||
* Base for templates with a feed
|
||||
*/
|
||||
class FeedTemplate extends BaseTemplate {
|
||||
public object $feed;
|
||||
public Feed $feed;
|
||||
|
||||
function __construct(string $title, object $feed) {
|
||||
function __construct(string $title, Feed $feed) {
|
||||
parent::__construct($title);
|
||||
$this->feed = $feed;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
namespace App\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;
|
||||
}
|
||||
}
|
||||
+22
-15
@@ -3,21 +3,21 @@
|
||||
<section class="section">
|
||||
<div class="columns is-multiline is-vcentered">
|
||||
{foreach $feed->items as $item}
|
||||
<div class="column is-one-quarter">
|
||||
<a class="clickable-img" id="{$item->id}" href="#{$item->id}"
|
||||
data-video_url="{path('/stream?url=' . urlencode($item->video->playAddr))}"
|
||||
data-video_download_watermark="{path('/stream?url=' . urlencode($item->video->playAddr) . '&download=1&id=' . $item->id . '&user=' . $item->author->uniqueId) . '&watermark='}"
|
||||
data-video_download_nowatermark="{path('/stream?url=' . urlencode('https://tiktok.com/@' . $item->author->uniqueId . '/' . $item->id) . '&download=1&id=' . $item->id . '&user=' . $item->author->uniqueId)}"
|
||||
data-desc="{$item->desc}"
|
||||
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" data-src="{path('/stream?url=' . urlencode($item->video->dynamicCover))}" />
|
||||
</a>
|
||||
{do $share_url = 'https://tiktok.com/@' . $item->author->uniqueId . '/video/' . $item->id}
|
||||
<div class="column is-one-quarter clickable-img" id="{$item->id}" onclick="openVideo(this.id)"
|
||||
data-video_url="{path('/stream?url=' . urlencode($item->video->playAddr))}"
|
||||
data-video_download_watermark="{path('/stream?url=' . urlencode($item->video->playAddr) . '&download=1&id=' . $item->id . '&user=' . $item->author->uniqueId) . '&watermark='}"
|
||||
data-video_download_nowatermark="{path('/stream?download=1&id=' . $item->id . '&user=' . $item->author->uniqueId)}"
|
||||
data-video_share_url="{$share_url}"
|
||||
data-desc="{$item->desc}"
|
||||
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" data-src="{path('/stream?url=' . urlencode($item->video->dynamicCover))}" />
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
<div n:ifset="$feed->info" class="buttons">
|
||||
<div class="buttons">
|
||||
{if isset($_GET['cursor']) && $_GET['cursor'] != 0 }
|
||||
<a class="button is-danger" href="?">First</a>
|
||||
<button class="button is-danger" onclick="history.back()">Back</button>
|
||||
@@ -35,15 +35,22 @@
|
||||
<p class="modal-card-title" id="item_title"></p>
|
||||
</header>
|
||||
<section class="modal-card-body has-text-centered" style="overflow: hidden;">
|
||||
<video id="video" controls preload="none"></video>
|
||||
<video id="video" autoplay controls></video>
|
||||
</section>
|
||||
<footer class="modal-card-foot has-text-centered">
|
||||
<div class="container">
|
||||
<div class="field has-addons has-addons-centered">
|
||||
<div class="control">
|
||||
<input id="share_input" class="input" readonly />
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-primary" onclick="copyShare()">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons is-centered">
|
||||
<a target="_blank" id="download_watermark" class="button is-info" download>Download with watermark</a>
|
||||
<a target="_blank" id="download_watermark" class="button is-info">Download with watermark</a>
|
||||
<a target="_blank" id="download_nowatermark" class="button is-info">Download without watermark</a>
|
||||
</div>
|
||||
<p>Please be patient with the No Watermark option. It takes a while to download</p>
|
||||
<p id="audio_title"></p>
|
||||
<audio id="audio" controls preload="none"></audio>
|
||||
<div class="buttons is-centered">
|
||||
|
||||
+2
-8
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "pablouser1/proxitok",
|
||||
"description": "An alternative frontend for TikTok",
|
||||
"version": "1.4.1.3",
|
||||
"version": "1.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"type": "project",
|
||||
"homepage": "https://github.com/pablouser1/ProxiTok",
|
||||
@@ -11,16 +11,10 @@
|
||||
"homepage": "https://github.com/pablouser1"
|
||||
}
|
||||
],
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/pablouser1/TikTok-API-PHP"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"ext-redis": "^5.3.2",
|
||||
"ext-mbstring": "*",
|
||||
"ssovit/tiktok-api": "dev-rework",
|
||||
"pablouser1/tikscraper": "^1.0",
|
||||
"latte/latte": "^2.10",
|
||||
"vlucas/phpdotenv": "^5.4",
|
||||
"bramus/router": "^1.6",
|
||||
|
||||
Generated
+45
-60
@@ -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": "94d34d2f322b7dfcf36f55780c95cd59",
|
||||
"content-hash": "f5e451a254a58bf7d512421237641801",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bramus/router",
|
||||
@@ -304,6 +304,45 @@
|
||||
},
|
||||
"time": "2016-11-19T20:47:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pablouser1/tikscraper",
|
||||
"version": "v1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pablouser1/TikScraperPHP.git",
|
||||
"reference": "2022b2c2d661383d09c4727091df7fae345844a5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pablouser1/TikScraperPHP/zipball/2022b2c2d661383d09c4727091df7fae345844a5",
|
||||
"reference": "2022b2c2d661383d09c4727091df7fae345844a5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.3|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"TikScraper\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Pablo Ferreiro"
|
||||
}
|
||||
],
|
||||
"description": "Get data from TikTok API",
|
||||
"support": {
|
||||
"issues": "https://github.com/pablouser1/TikScraperPHP/issues",
|
||||
"source": "https://github.com/pablouser1/TikScraperPHP/tree/v1.2.1"
|
||||
},
|
||||
"time": "2022-02-13T20:28:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.8.1",
|
||||
@@ -375,58 +414,6 @@
|
||||
],
|
||||
"time": "2021-12-04T23:24:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ssovit/tiktok-api",
|
||||
"version": "dev-rework",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pablouser1/TikTok-API-PHP.git",
|
||||
"reference": "6d2be1b886e455d6e124453dff07de3e89c0d45a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pablouser1/TikTok-API-PHP/zipball/6d2be1b886e455d6e124453dff07de3e89c0d45a",
|
||||
"reference": "6d2be1b886e455d6e124453dff07de3e89c0d45a",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Sovit\\": "lib"
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": [
|
||||
"/example",
|
||||
"/.github",
|
||||
"/.editorconfig"
|
||||
]
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sovit Tamrakar",
|
||||
"email": "sovit.tamrakar@gmail.com",
|
||||
"homepage": "https://github.com/ssovit/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Unofficial TikTok API for PHP",
|
||||
"homepage": "https://github.com/ssovit/TikTok-API-PHP",
|
||||
"keywords": [
|
||||
"tiktok",
|
||||
"tiktok-api",
|
||||
"tiktok-scraper"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/ssovit/TikTok-API-PHP",
|
||||
"issues": "https://github.com/ssovit/TikTok-API-PHP/issues",
|
||||
"email": "sovit.tamrakar@gmail.com"
|
||||
},
|
||||
"time": "2022-02-07T20:07:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.24.0",
|
||||
@@ -543,12 +530,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -759,9 +746,7 @@
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"ssovit/tiktok-api": 20
|
||||
},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
||||
+39
-53
@@ -1,3 +1,5 @@
|
||||
var opened_video_id = null
|
||||
|
||||
const video = document.getElementById('video')
|
||||
const item_title = document.getElementById('item_title')
|
||||
const audio = document.getElementById('audio')
|
||||
@@ -5,13 +7,12 @@ const audio_title = document.getElementById('audio_title')
|
||||
const modal = document.getElementById('modal')
|
||||
const download_watermark = document.getElementById('download_watermark')
|
||||
const download_nowatermark = document.getElementById('download_nowatermark')
|
||||
const share_input = document.getElementById('share_input')
|
||||
|
||||
// -- HELPERS -- //
|
||||
const getHash = () => window.location.hash.substring(1)
|
||||
|
||||
const getVideoDataById = (id) => {
|
||||
const getVideoDataById = id => {
|
||||
const el = document.getElementById(id)
|
||||
if (el) {
|
||||
opened_video_id = id
|
||||
return el.dataset
|
||||
}
|
||||
return false
|
||||
@@ -19,19 +20,20 @@ const getVideoDataById = (id) => {
|
||||
|
||||
const isModalActive = () => modal.classList.contains('is-active')
|
||||
|
||||
const toggleButton = (id, force) => document.getElementById(id) ? document.getElementById(id).toggleAttribute('disabled', force) : alert('That button does not exist')
|
||||
const toggleButton = (id, force) => document.getElementById(id).toggleAttribute('disabled', force)
|
||||
|
||||
// -- MODAL -- //
|
||||
const swapData = ({ video_url, desc, video_download_watermark, video_download_nowatermark, music_title, music_url }) => {
|
||||
const swapData = ({ video_url, desc, video_download_watermark, video_download_nowatermark, video_share_url, music_title, music_url }) => {
|
||||
video.src = video_url
|
||||
item_title.innerText = desc
|
||||
download_watermark.href = video_download_watermark
|
||||
download_nowatermark.href = video_download_nowatermark
|
||||
share_input.value = video_share_url
|
||||
audio_title.innerText = music_title
|
||||
audio.src = music_url
|
||||
}
|
||||
|
||||
const showModal = (id) => {
|
||||
const showModal = id => {
|
||||
const dataset = getVideoDataById(id)
|
||||
if (dataset) {
|
||||
swapData(dataset)
|
||||
@@ -50,27 +52,27 @@ const hideModel = () => {
|
||||
history.pushState('', document.title, window.location.pathname + window.location.search)
|
||||
}
|
||||
|
||||
const getPrevOrNext = (forward) => {
|
||||
const hash = getHash()
|
||||
if (hash) {
|
||||
const el = document.getElementById(hash)
|
||||
const getPrevOrNext = forward => {
|
||||
if (opened_video_id) {
|
||||
const el = document.getElementById(opened_video_id)
|
||||
if (el) {
|
||||
if (forward) {
|
||||
return el.parentElement.nextElementSibling ? el.parentElement.nextElementSibling.children[0] : null
|
||||
return el.nextElementSibling
|
||||
}
|
||||
return el.parentElement.previousElementSibling ? el.parentElement.previousElementSibling.children[0] : null
|
||||
return el.previousElementSibling
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const moveVideo = (forward) => {
|
||||
const moveVideo = forward => {
|
||||
// Re-enable buttons
|
||||
toggleButton('back-button', false)
|
||||
toggleButton('next-button', false)
|
||||
const new_el = getPrevOrNext(forward)
|
||||
if (new_el) {
|
||||
window.location.hash = new_el.id
|
||||
opened_video_id = new_el.id
|
||||
swapData(new_el.dataset)
|
||||
} else {
|
||||
// Max reached, disable buttons depending on direction
|
||||
if (forward) {
|
||||
@@ -82,58 +84,42 @@ const moveVideo = (forward) => {
|
||||
}
|
||||
|
||||
// EVENTS //
|
||||
const hashChange = () => {
|
||||
if (window.location.hash) {
|
||||
const hash = getHash()
|
||||
if (hash) {
|
||||
// Check first if the modal is already active
|
||||
if (isModalActive()) {
|
||||
// If it is, get hash video id and swap data
|
||||
const dataset = getVideoDataById(hash)
|
||||
if (dataset) {
|
||||
swapData(dataset)
|
||||
video.play()
|
||||
}
|
||||
} else {
|
||||
showModal(hash)
|
||||
}
|
||||
const openVideo = video_id => {
|
||||
if (isModalActive()) {
|
||||
const dataset = getVideoDataById(video_id)
|
||||
if (dataset) {
|
||||
swapData(dataset)
|
||||
}
|
||||
} else {
|
||||
showModal(video_id)
|
||||
}
|
||||
}
|
||||
|
||||
const swapImages = (e, mode) => {
|
||||
let img = null
|
||||
let gif = null
|
||||
if (mode === 'gif') {
|
||||
const a = e.target
|
||||
img = a.children[0]
|
||||
gif = a.children[1]
|
||||
} else if (mode === 'img') {
|
||||
gif = e.target
|
||||
img = e.target.parentElement.children[0]
|
||||
const swapImages = e => {
|
||||
const div = e.target
|
||||
const img = div.children[0]
|
||||
const gif = div.children[1]
|
||||
if (!gif.src) {
|
||||
gif.src = gif.dataset.src
|
||||
}
|
||||
img.classList.toggle('hidden')
|
||||
gif.classList.toggle('hidden')
|
||||
}
|
||||
|
||||
if (img && gif) {
|
||||
if (!gif.src) {
|
||||
gif.src = gif.dataset.src
|
||||
}
|
||||
img.classList.toggle('hidden')
|
||||
gif.classList.toggle('hidden')
|
||||
}
|
||||
const copyShare = () => {
|
||||
share_input.select();
|
||||
navigator.clipboard.writeText(share_input.value);
|
||||
alert('Copied!')
|
||||
}
|
||||
|
||||
document.getElementById('modal-background').addEventListener('click', hideModel)
|
||||
document.getElementById('modal-close').addEventListener('click', hideModel)
|
||||
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)
|
||||
images[i].addEventListener('mouseenter', swapImages, false)
|
||||
images[i].addEventListener('mouseout', swapImages, false)
|
||||
}
|
||||
|
||||
|
||||
hashChange()
|
||||
|
||||
+5
-1
@@ -12,8 +12,12 @@ figure {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Make gifs take all available space */
|
||||
.clickable-img {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clickable-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
<p>
|
||||
This project wouldn't be possible without the help of the following projects:
|
||||
<ul>
|
||||
<li><a rel="nofollow" href="https://github.com/ssovit/TikTok-API-PHP">TikTok-API-PHP</a></li>
|
||||
<li><a rel="nofollow" href="https://github.com/pablouser1/TikScraperPHP">TikScraper</a></li>
|
||||
<li><a rel="nofollow" href="https://github.com/nette/latte">Latte</a></li>
|
||||
<li><a rel="nofollow" href="https://github.com/bramus/router">bramus/router</a></li>
|
||||
<li><a rel="nofollow" href="https://github.com/vlucas/phpdotenv">PHP dotenv</a></li>
|
||||
|
||||
+3
-3
@@ -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->title}</p>
|
||||
<p class="subtitle">{$feed->info->detail->desc}</p>
|
||||
<p>Videos: {number($feed->info->stats->videoCount)}</p>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
{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>
|
||||
<a href="{path('tag/' . $feed->info->detail->challenge->title . '/rss')}">RSS</a>
|
||||
<p class="title">{$feed->info->detail->title}</p>
|
||||
<p class="subtitle">{$feed->info->detail->desc}</p>
|
||||
<p>Videos: {number($feed->info->stats->videoCount)} / Views: {number($feed->info->stats->viewCount)}</p>
|
||||
<a href="{path('tag/' . $feed->info->detail->title . '/rss')}">RSS</a>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
||||
+6
-6
@@ -2,13 +2,13 @@
|
||||
|
||||
{block header}
|
||||
<figure class="figure is-96x96">
|
||||
<img src="{path('/stream?url=' . urlencode($feed->info->detail->user->avatarThumb))}" />
|
||||
<img src="{path('/stream?url=' . urlencode($feed->info->detail->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>
|
||||
<a href="{path('/@' . $feed->info->detail->user->uniqueId . '/rss')}">RSS</a>
|
||||
<p class="title">{$feed->info->detail->uniqueId}'s profile</p>
|
||||
<p class="subtitle">{$feed->info->detail->signature}</p>
|
||||
<p>Following: {number($feed->info->stats->followingCount)} / Followers: {number($feed->info->stats->followerCount)}</p>
|
||||
<p>Hearts: {number($feed->info->stats->heartCount)} / Videos: {$feed->info->stats->videoCount}</p>
|
||||
<a href="{path('/@' . $feed->info->detail->uniqueId . '/rss')}">RSS</a>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
||||
+9
-9
@@ -3,20 +3,20 @@
|
||||
{block content}
|
||||
<div class="columns is-centered is-vcentered">
|
||||
<div class="column">
|
||||
<video controls poster="{path('/stream?url=' . urlencode($item->items[0]->video->originCover))}">
|
||||
<source src="{path('/stream?url=' . urlencode($item->items[0]->video->playAddr))}" type="video/mp4" />
|
||||
<video controls poster="{path('/stream?url=' . urlencode($feed->items[0]->video->originCover))}">
|
||||
<source src="{path('/stream?url=' . urlencode($feed->items[0]->video->playAddr))}" type="video/mp4" />
|
||||
</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>
|
||||
<p class="title">Video by <a href="{path('/@'.$feed->info->detail->uniqueId)}">{$feed->info->detail->uniqueId}</a></p>
|
||||
<p class="subtitle">{$feed->items[0]->desc}</p>
|
||||
<p>Played {number($feed->info->stats->playCount)} times</p>
|
||||
<p>Shared {number($feed->info->stats->shareCount)} times / {number($feed->info->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>
|
||||
<a href="{path('/stream?url=' . urlencode($feed->items[0]->video->playAddr) . '&download=1')}" class="button is-info">Download video</a>
|
||||
<p>{$feed->items[0]->music->title}</p>
|
||||
<audio src="{path('/stream?url=' . urlencode($feed->items[0]->music->playUrl))}" controls preload="none"></audio>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user