mirror of
https://github.com/pablouser1/ProxiTok.git
synced 2024-12-06 19:27:30 +01:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94da0e46ac | |||
| 1176bc35fe | |||
| 4bfc4c9434 | |||
| de85ba3e44 | |||
| 79fd699cee | |||
| 8f83a27885 | |||
| cba59e66c7 | |||
| c925ca2a41 | |||
| 32ea8b6b12 | |||
| 7d0df67ded | |||
| 23583ef7ab | |||
| 6fafcdcd11 | |||
| 5e1b009f75 | |||
| 7aa869f567 | |||
| c076ba65a6 | |||
| 2504ba6e93 | |||
| 38557fafa6 | |||
| 96fb2fd428 | |||
| af4d98b8dd | |||
| 40e5d45956 | |||
| 2f20cf8c02 | |||
| aa2fff2b31 | |||
| fbc9061b21 | |||
| 67c080e5f4 | |||
| 086c95ca71 | |||
| 931191bcaf | |||
| ebe5941fa2 | |||
| 574ae51582 | |||
| 90a2d2ec28 | |||
| 3880da35ea | |||
| 8d8fe43431 | |||
| e7ee03e3cc | |||
| d0057e77e3 | |||
| d52dc8687f | |||
| bc3777d74c | |||
| 9acc40618a | |||
| 5e33b93031 | |||
| 76bfcbe34a | |||
| e2282f9dcc | |||
| cfe3a0c51b | |||
| 3b0ed28ad9 | |||
| 9d37bc433d | |||
| 26bde1e4a8 | |||
| 59029e48ad | |||
| e12d26562d | |||
| bdba336d3c | |||
| 578277d251 | |||
| 240b8d7540 | |||
| 08a28ffc3a | |||
| e1fbf75281 | |||
| c041e84967 | |||
| 6c7c0272d4 | |||
| f1728257f2 | |||
| 0c100d650b | |||
| 55e1e9057b | |||
| 14a8829087 | |||
| 367fe8d9de | |||
| abf23450db | |||
| 177f290e56 | |||
| cd524c8da8 | |||
| 33d5994159 | |||
| 62a6f6b9dd | |||
| 9a662ffe1b | |||
| 4232ca8d64 | |||
| 47a3dc7e76 | |||
| 288add48d5 | |||
| e545bd6ddd | |||
| c04122d11f | |||
| d9165d693f | |||
| 717997685a | |||
| 2c68b34d06 | |||
| 816c5cf55f | |||
| 568c9c9ea9 | |||
| 47a232de3d | |||
| 3730c95f29 | |||
| d0d36c26f3 | |||
| 5368c08039 | |||
| bed4ef9d3a | |||
| fdcb33fb18 | |||
| bc4468c5ab | |||
| 1a86abeb2d | |||
| 44ee065ec6 | |||
| 9a35c61023 | |||
| 9f43caa3c4 | |||
| 44429cb7e4 | |||
| ade2b3c01a | |||
| e4e6f05362 | |||
| eb69e8a22e | |||
| 5fffc7ae08 | |||
| f0765e266c | |||
| c26e027c01 | |||
| 6eee156541 | |||
| c682432920 | |||
| d061c210a2 | |||
| 3b5c3cd96c | |||
| 1b29950d89 | |||
| 8ece2c2b25 | |||
| 7ba324c935 | |||
| 9c95620384 | |||
| 7b5ec2211e | |||
| 2bdd433a17 | |||
| b8f8eb710f | |||
| c62df9d631 | |||
| 605e204453 | |||
| 7011867fd8 | |||
| a4a4b0497c | |||
| f1de8e979b | |||
| 1e1376506c | |||
| 198c88a1d7 | |||
| 73dd93420f | |||
| db3799c344 | |||
| 1eb6626153 | |||
| c413017b00 | |||
| 669c0d29d4 | |||
| 58170665ed | |||
| d60e4400a2 | |||
| c47d6cd6d4 | |||
| 279a4f50c6 | |||
| 9711bb903c | |||
| a11a17f9d2 | |||
| c690adab63 | |||
| 1d37d8c417 | |||
| 90cf0bfb56 | |||
| a3ebb08d26 | |||
| 19e2c32c77 | |||
| 0c4616c695 | |||
| fccec753d9 | |||
| 7203bc1509 | |||
| d871ed4e68 | |||
| 2c73bd0388 | |||
| dd5964cb97 | |||
| 19553f8837 |
@@ -0,0 +1,8 @@
|
||||
node_modules
|
||||
/.env
|
||||
/.vscode
|
||||
/vendor
|
||||
/cache/latte/*
|
||||
!/cache/latte/.gitkeep
|
||||
/cache/api/*
|
||||
!/cache/api/.gitkeep
|
||||
+13
-3
@@ -1,7 +1,11 @@
|
||||
# APP_URL="http://localhost:8000" # Full url path, PLEASE REPLACE TO YOUR OWN ONE
|
||||
|
||||
# APP_URL="http://localhost:8000" # Absolute path to proxitok instance
|
||||
# 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)
|
||||
|
||||
# API CONFIG
|
||||
# API_SIGNER="remote"
|
||||
# API_SIGNER_URL="http://localhost:8080"
|
||||
# API_TEST_ENDPOINTS=true # Uncomment for usage of testing TikTok endpoints, may help sometimes
|
||||
# API_CACHE=redis
|
||||
|
||||
# Redis cache, used on Helpers\CacheEngines\RedisCache (CHOOSE ONE)
|
||||
# REDIS_HOST=localhost # Host or path to unix socket
|
||||
@@ -10,3 +14,9 @@
|
||||
|
||||
# JSON cache, used on Helpers\CacheEngines\JSONCache
|
||||
# API_CACHE_JSON=/tmp/proxitok_api # Path for JSON API Cache, leave commented for ./cache/api
|
||||
|
||||
# Proxy Config
|
||||
# PROXY_HOST=http://EXAMPLE_IP # Host to be used as proxy, make sure to type a valid protocol. Available: http:// | https:// | socks4:// | socks5://
|
||||
# PROXY_PORT=8080
|
||||
# PROXY_USERNAME=username
|
||||
# PROXY_PASSWORD=password
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
liberapay: 'pablouser1'
|
||||
custom: 'https://paypal.me/pablouser1'
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
name: Add my instance
|
||||
about: Add public instance to the list
|
||||
title: "[INSTANCES] New public instance"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- URL: https://proxitok.example.com
|
||||
- Cloudflare: Yes / No
|
||||
- Country: Type the name of the country you are hosting the instance here
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: pablouser1
|
||||
|
||||
---
|
||||
|
||||
Before submitting an issue, please make sure you are using the latest version of ProxiTok.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Logs**
|
||||
If applicable, add PHP logs to help explain the issue.
|
||||
|
||||
**Enviroment (please complete the following information):**
|
||||
|
||||
- Deployment method (if possible): [e.g docker, heroku, manual...]
|
||||
- Frontend version: [e.g. 2.2.0.0]
|
||||
- Scrapper version: [e.g. 2.4.0.0]
|
||||
- PHP Version: [e.g. 8.0]
|
||||
|
||||
You can find the frontend and scrapper version on /about
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
@@ -0,0 +1,102 @@
|
||||
name: Docker
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.13.1'
|
||||
|
||||
|
||||
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Set some variables for the image
|
||||
run: |
|
||||
echo "IMAGE_VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||
echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: |
|
||||
ghcr.io/${{ env.IMAGE_NAME }}:latest
|
||||
ghcr.io/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
# Sign the resulting Docker image digest except on PRs.
|
||||
# This will only write to the public Rekor transparency log when the Docker
|
||||
# repository is public to avoid leaking data. If you would like to publish
|
||||
# transparency data even for private images, pass --force to cosign below.
|
||||
# https://github.com/sigstore/cosign
|
||||
- name: Sign the published Docker image
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
# This step uses the identity token to provision an ephemeral certificate
|
||||
# against the sigstore community Fulcio instance.
|
||||
run: cosign sign ${{ steps.meta.outputs.tags }}@${{ steps.build-and-push.outputs.digest }}
|
||||
@@ -20,6 +20,6 @@ jobs:
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: v${{steps.version.outputs.prop}}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowUpdates: true
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
RewriteEngine on
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule . index.php [L]
|
||||
RewriteRule ^ index.php [QSA,L]
|
||||
|
||||
# Disable index view
|
||||
Options -Indexes
|
||||
|
||||
# Hide a specific file
|
||||
<Files .env>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
FROM php:8.1-apache
|
||||
WORKDIR /var/www/html
|
||||
COPY --from=composer /usr/bin/composer /usr/bin/composer
|
||||
RUN apt update -y && apt upgrade -y \
|
||||
&& apt install -y --no-install-recommends libzip-dev \
|
||||
&& pecl install redis zip \
|
||||
&& docker-php-ext-enable redis zip \
|
||||
&& a2enmod rewrite headers \
|
||||
&& mkdir /cache \
|
||||
&& chown -R www-data:www-data /cache \
|
||||
&& rm -rf /var/www/html/*
|
||||
|
||||
# Copy project to /var/www/html
|
||||
COPY . .
|
||||
|
||||
# Run composer and clean
|
||||
RUN composer update --no-cache \
|
||||
&& composer install --no-interaction --optimize-autoloader --no-dev --no-cache \
|
||||
&& apt autoclean -y \
|
||||
&& apt autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -rf /usr/bin/composer
|
||||
|
||||
EXPOSE 80
|
||||
CMD apachectl -D FOREGROUND
|
||||
@@ -1 +1 @@
|
||||
web: vendor/bin/heroku-php-apache2
|
||||
web: vendor/bin/heroku-php-nginx -C setup/heroku/nginx.conf
|
||||
|
||||
@@ -4,65 +4,46 @@ Use Tiktok with an alternative frontend, inspired by Nitter.
|
||||
## Features
|
||||
* Privacy: All requests made to TikTok are server-side, so you will never connect to their servers
|
||||
* See user's feed
|
||||
* See trending
|
||||
* See trending and discovery tab
|
||||
* See tags
|
||||
* See video by id
|
||||
* Discovery
|
||||
* Create a following list, which you can later use to see all the feeds from those users
|
||||
* Themes
|
||||
* RSS Feed for user, trending and tag (just add /rss to the url)
|
||||
|
||||
## Installation
|
||||
Clone the repository and fetch the requiered external packages with:
|
||||
```bash
|
||||
composer install
|
||||
## Self-hosting
|
||||
Please check [this](https://github.com/pablouser1/ProxiTok/wiki/Self-hosting) wiki article for info on how to self-host your own instance
|
||||
|
||||
## Public instances
|
||||
[This](https://github.com/pablouser1/ProxiTok/wiki/Public-instances) wiki article contains a list with all the known public instances.
|
||||
|
||||
## Extensions
|
||||
If you want to automatically redirect Tiktok links to ProxiTok you can use:
|
||||
* [Libredirect](https://github.com/libredirect/libredirect)
|
||||
* [Redirector](https://github.com/einaregilsson/Redirector)
|
||||
|
||||
You can use the following config if you want to use Redirector (you can change https://proxitok.pabloferreiro.es with whatever instance you want to use):
|
||||
```
|
||||
Description: TikTok to ProxiTok
|
||||
Example URL: https://www.tiktok.com/@tiktok
|
||||
Include pattern: (.*//.*)(tiktok.com)(.*)
|
||||
Redirect to: https://proxitok.pabloferreiro.es$3
|
||||
Example result: https://proxitok.pabloferreiro.es/@tiktok
|
||||
Pattern type: Regular Expression
|
||||
Apply to: Main window (address bar)
|
||||
```
|
||||
|
||||
WARNING: You'll need a personal Github token for composer.
|
||||
|
||||
Then you can run it using for example the PHP Development Server with:
|
||||
```bash
|
||||
php -S localhost:8080
|
||||
```
|
||||
|
||||
## Configuration
|
||||
### .env
|
||||
Move the .env.example file to .env and modify it.
|
||||
|
||||
### Cache engines
|
||||
Available cache engines:
|
||||
* redis: Writes response to Redis
|
||||
* json: Writes response to JSON file
|
||||
|
||||
### Apache
|
||||
You don't have to do anything more
|
||||
|
||||
### Nginx
|
||||
Add the following to your config (you can modify the proxitok part if you have or not a subdir):
|
||||
```
|
||||
location /proxitok {
|
||||
return 302 $scheme://$host/proxitok/;
|
||||
}
|
||||
|
||||
location /proxitok/ {
|
||||
try_files $uri $uri/ /proxitok/index.php?$query_string;
|
||||
}
|
||||
|
||||
location /proxitok/.env {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
```
|
||||
|
||||
## TODO
|
||||
* Add a NoJS version / Make the whole program without required JS
|
||||
* Better error handling
|
||||
## TODO / Known issues
|
||||
* Replace placeholder favicon
|
||||
* Make video on /video fit screen and don't overflow
|
||||
* i18n
|
||||
* Fix embed styling
|
||||
* Fix crash when invalid vm.tiktok.com/CODE or www.tiktok.com/t/CODE is provided
|
||||
* Add custom amount of videos per page
|
||||
|
||||
## Credits
|
||||
* [TikTok-API-PHP](https://github.com/ssovit/TikTok-API-PHP) (Currently using my personal fork)
|
||||
[@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
|
||||
### External libraries
|
||||
* [TikScraperPHP](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)
|
||||
* [Bulma](https://github.com/jgthms/bulma) and [Bulmaswatch](https://github.com/jenil/bulmaswatch)
|
||||
* [FeedWriter](https://github.com/mibe/FeedWriter)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "ProxiTok",
|
||||
"description": "Open source alternative frontend for TikTok",
|
||||
"keywords": [
|
||||
"php",
|
||||
"tiktok",
|
||||
"alternative-frontends"
|
||||
],
|
||||
"website": "https://github.com/pablouser1/ProxiTok",
|
||||
"repository": "https://github.com/pablouser1/ProxiTok",
|
||||
"env": {
|
||||
"APP_URL": {
|
||||
"description": "Your instance url (example: https://proxitok.herokuapp.com)"
|
||||
},
|
||||
"API_CACHE": {
|
||||
"description": "Cache system in use",
|
||||
"value": "redis"
|
||||
},
|
||||
"LATTE_CACHE": {
|
||||
"description": "Latte cache path",
|
||||
"value": "/tmp/latte"
|
||||
},
|
||||
"API_SIGNER": {
|
||||
"description": "Signer method",
|
||||
"value": "remote"
|
||||
},
|
||||
"API_SIGNER_URL": {
|
||||
"description": "Remote signer url",
|
||||
"value": "https://signtok.vercel.app/api"
|
||||
}
|
||||
},
|
||||
"addons": [
|
||||
{
|
||||
"plan": "heroku-redis:hobby-dev",
|
||||
"as": "REDIS"
|
||||
}
|
||||
],
|
||||
"buildpacks": [
|
||||
{
|
||||
"url": "heroku/php"
|
||||
}
|
||||
]
|
||||
}
|
||||
+13
-14
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace App\Cache;
|
||||
|
||||
class JSONCache {
|
||||
use TikScraper\Interfaces\CacheInterface;
|
||||
|
||||
class JSONCache implements CacheInterface {
|
||||
private string $cache_path = __DIR__ . '/../../cache/api';
|
||||
|
||||
function __construct() {
|
||||
@@ -9,25 +11,22 @@ class JSONCache {
|
||||
$this->cache_path = $_ENV['API_CACHE_JSON'];
|
||||
}
|
||||
}
|
||||
public function get(string $cache_key): object|false {
|
||||
public function get(string $cache_key): ?object {
|
||||
$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 $element;
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace App\Cache;
|
||||
|
||||
class RedisCache {
|
||||
use TikScraper\Interfaces\CacheInterface;
|
||||
|
||||
class RedisCache implements CacheInterface {
|
||||
private \Redis $client;
|
||||
function __construct(string $host, int $port, ?string $password) {
|
||||
$this->client = new \Redis();
|
||||
@@ -21,13 +23,14 @@ class RedisCache {
|
||||
|
||||
public function get(string $cache_key): ?object {
|
||||
$data = $this->client->get($cache_key);
|
||||
if ($data) {
|
||||
return json_decode($data);
|
||||
}
|
||||
return null;
|
||||
return $data ? json_decode($data) : null;
|
||||
}
|
||||
|
||||
public function set(string $cache_key, array $data, $timeout = 3600) {
|
||||
$this->client->set($cache_key, json_encode($data), $timeout);
|
||||
public function exists(string $cache_key): bool {
|
||||
return $this->client->exists($cache_key);
|
||||
}
|
||||
|
||||
public function set(string $cache_key, string $data, $timeout = 3600) {
|
||||
$this->client->set($cache_key, $data, $timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace App\Constants;
|
||||
|
||||
abstract class CacheMethods {
|
||||
const JSON = 'json';
|
||||
const REDIS = 'redis';
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace App\Constants;
|
||||
|
||||
abstract class Themes {
|
||||
const DEFAULT = "default";
|
||||
const CARD = "card";
|
||||
}
|
||||
@@ -2,18 +2,17 @@
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\FeedTemplate;
|
||||
|
||||
class DiscoverController {
|
||||
static public function get() {
|
||||
$api = Misc::api();
|
||||
$feed = $api->getDiscover();
|
||||
$api = Wrappers::api();
|
||||
$feed = $api->discover();
|
||||
if ($feed->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('discover'), new FeedTemplate('Discover', $feed));
|
||||
Wrappers::latte('discover', new FeedTemplate('Discover', $feed));
|
||||
} else {
|
||||
ErrorHandler::show($feed->meta);
|
||||
ErrorHandler::showMeta($feed->meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\VideoTemplate;
|
||||
|
||||
class EmbedController {
|
||||
static public function v2(int $id) {
|
||||
$api = Wrappers::api();
|
||||
$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));
|
||||
} else {
|
||||
ErrorHandler::showMeta($video->error());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,21 @@ namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Models\FeedTemplate;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\FullTemplate;
|
||||
|
||||
class MusicController {
|
||||
static public function get(string $music_id) {
|
||||
$cursor = Misc::getCursor();
|
||||
|
||||
$api = Misc::api();
|
||||
$feed = $api->getMusicFeed($music_id, $cursor);
|
||||
if ($feed->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('music'), new FeedTemplate('Music', $feed));
|
||||
$api = Wrappers::api();
|
||||
$music = $api->music($music_id);
|
||||
$music->feed($cursor);
|
||||
if ($music->ok()) {
|
||||
$data = $music->getFull();
|
||||
Wrappers::latte('music', new FullTemplate('Music', $data));
|
||||
} else {
|
||||
ErrorHandler::show($feed->meta);
|
||||
ErrorHandler::showMeta($music->error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Helpers\Cookies;
|
||||
use TikScraper\Helpers\Converter;
|
||||
|
||||
class ProxyController {
|
||||
const VALID_TIKTOK_DOMAINS = [
|
||||
"tiktokcdn.com", "tiktokcdn-us.com", "tiktok.com"
|
||||
@@ -9,27 +12,52 @@ class ProxyController {
|
||||
static private function isValidDomain(string $url) {
|
||||
$host = parse_url($url, PHP_URL_HOST);
|
||||
$host_split = explode('.', $host);
|
||||
return count($host_split) === 3 && in_array($host_split[1] . '.' . $host_split[2], self::VALID_TIKTOK_DOMAINS);
|
||||
$host_count = count($host_split);
|
||||
if ($host_count === 2) {
|
||||
// Using no watermark
|
||||
return in_array($host_split[0] . '.' . $host_split[1], self::VALID_TIKTOK_DOMAINS);
|
||||
} elseif ($host_count === 3) {
|
||||
return in_array($host_split[1] . '.' . $host_split[2], self::VALID_TIKTOK_DOMAINS);
|
||||
}
|
||||
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');
|
||||
}
|
||||
|
||||
if (isset($_GET['download'])) {
|
||||
// Download
|
||||
$downloader = new \Sovit\TikTok\Download();
|
||||
$downloader->url($url, "tiktok-video", 'mp4');
|
||||
} else {
|
||||
// Stream
|
||||
$streamer = new \Sovit\TikTok\Stream();
|
||||
$streamer->stream($url);
|
||||
}
|
||||
}
|
||||
|
||||
static private function getFilename(string $id, string $user): string {
|
||||
$filename = 'tiktok-video-' . $id . '-' . $user;
|
||||
return $filename;
|
||||
}
|
||||
|
||||
static public function stream() {
|
||||
self::checkUrl();
|
||||
$url = $_GET['url'];
|
||||
$streamer = new \TikScraper\Stream();
|
||||
$streamer->url($url);
|
||||
}
|
||||
|
||||
static public function download() {
|
||||
self::checkUrl();
|
||||
$method = Cookies::downloader();
|
||||
$downloader = new \TikScraper\Download($method);
|
||||
|
||||
// Params
|
||||
$id = $_GET['id'] ?? '';
|
||||
$watermark = isset($_GET['watermark']);
|
||||
$url = $_GET['url'];
|
||||
$user = $_GET['user'] ?? '';
|
||||
// Filename
|
||||
$filename = self::getFilename($id, $user);
|
||||
// Running
|
||||
$downloader->url($url, $filename, $watermark);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
|
||||
/**
|
||||
@@ -7,18 +8,72 @@ use App\Helpers\Misc;
|
||||
*/
|
||||
class RedirectController {
|
||||
static public function redirect() {
|
||||
$endpoint = '';
|
||||
if (isset($_GET['user'])) {
|
||||
$endpoint = '/@' . $_GET['user'];
|
||||
} else if (isset($_GET['tag'])) {
|
||||
$endpoint = '/tag/' . $_GET['tag'];
|
||||
} else if (isset($_GET['music'])) {
|
||||
$endpoint = '/music/' . $_GET['music'];
|
||||
} else if (isset($_GET['video'])) {
|
||||
$endpoint = '/video/' . $_GET['video'];
|
||||
$endpoint = '/';
|
||||
if (isset($_GET['type'], $_GET['term'])) {
|
||||
$term = trim($_GET['term']);
|
||||
switch ($_GET['type']) {
|
||||
case 'url':
|
||||
$endpoint = self::to_endpoint($term);
|
||||
if (!$endpoint) {
|
||||
ErrorHandler::showText(400, 'Invalid TikTok URL');
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'user':
|
||||
// Remove @ if sent
|
||||
if ($term[0] === '@') {
|
||||
$term = substr($term, 1);
|
||||
}
|
||||
$endpoint = '/@' . urlencode($term);
|
||||
break;
|
||||
case 'tag':
|
||||
// Remove # if sent
|
||||
if ($term[0] === '#') {
|
||||
$term = substr($term, 1);
|
||||
}
|
||||
$endpoint = '/tag/' . urlencode($term);
|
||||
break;
|
||||
case 'music':
|
||||
$endpoint = '/music/' . urlencode($term);
|
||||
break;
|
||||
case 'video':
|
||||
// The @username part is not used, but
|
||||
// it is the schema that TikTok follows
|
||||
$endpoint = '/@placeholder/video/' . urlencode($term);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$url = Misc::url($endpoint);
|
||||
header("Location: {$url}");
|
||||
}
|
||||
|
||||
/**
|
||||
* to_endpoint maps a TikTok URL into a ProxiTok-compatible endpoint URL.
|
||||
*/
|
||||
static private function to_endpoint(string $url): string {
|
||||
if (preg_match('%^(?:https?://|www\.)?(?:vm\.|vt\.)?tiktok\.com/(?:t/)?([A-Za-z0-9]+)%', $url, $m)) {
|
||||
// Short video URL
|
||||
return '/@placeholder/video/' . $m[1];
|
||||
} elseif (preg_match('%^https://www\.tiktok\.com/(.+)%', $url, $m)) {
|
||||
// Username component (which may indicate a user profile URL or a video URL)
|
||||
if (preg_match('%^(@[A-Za-z0-9_.]+)(?:/|$)(.*)%', $m[1], $u)) {
|
||||
if ($u[2] == '') {
|
||||
// User profile URL
|
||||
return '/' . urlencode($u[1]);
|
||||
} elseif (preg_match('%^video/(\d+)%', $u[2], $v)) {
|
||||
// Video URL
|
||||
return '/' . urlencode($u[1]) . '/video/' . urlencode($v[1]);
|
||||
}
|
||||
} elseif (preg_match('%^tag/([^ ]+?)(?:/|$)%', $m[1], $t)) {
|
||||
// Tag URL
|
||||
return '/tag/' . urlencode($t[1]);
|
||||
} elseif (preg_match('%^music/([^ ]+?)(?:/|$)%', $m[1], $m)) {
|
||||
// Music URL
|
||||
return '/music/' . urlencode($m[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,37 @@ namespace App\Controllers;
|
||||
|
||||
use App\Helpers\Misc;
|
||||
use App\Helpers\Cookies;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\SettingsTemplate;
|
||||
|
||||
class SettingsController {
|
||||
static public function index() {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('settings'), new SettingsTemplate());
|
||||
Wrappers::latte('settings', new SettingsTemplate());
|
||||
}
|
||||
|
||||
static public function proxy() {
|
||||
if (in_array(Cookies::PROXY, $_POST)) {
|
||||
foreach (Cookies::PROXY as $proxy_element) {
|
||||
Cookies::set($proxy_element, $_POST[$proxy_element]);
|
||||
}
|
||||
static public function general() {
|
||||
if (isset($_POST['theme'])) {
|
||||
$theme = $_POST['theme'];
|
||||
Cookies::set('theme', $theme);
|
||||
}
|
||||
self::redirect();
|
||||
}
|
||||
|
||||
static public function api() {
|
||||
// TODO, ADD COUNT
|
||||
if (isset($_POST['api-test_endpoints'])) {
|
||||
$test_endpoints = $_POST['api-test_endpoints'];
|
||||
Cookies::set('api-test_endpoints', $test_endpoints);
|
||||
}
|
||||
|
||||
if (isset($_POST['api-downloader'])) {
|
||||
$downloader = $_POST['api-downloader'];
|
||||
Cookies::set("api-downloader", $downloader);
|
||||
}
|
||||
self::redirect();
|
||||
}
|
||||
|
||||
static private function redirect() {
|
||||
$url = Misc::url('/settings');
|
||||
header("Location: {$url}");
|
||||
}
|
||||
|
||||
@@ -3,30 +3,33 @@ namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Helpers\RSS;
|
||||
use App\Models\FeedTemplate;
|
||||
use App\Helpers\UrlBuilder;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\FullTemplate;
|
||||
use App\Models\RSSTemplate;
|
||||
|
||||
class TagController {
|
||||
static public function get(string $name) {
|
||||
$cursor = Misc::getCursor();
|
||||
$api = Misc::api();
|
||||
$feed = $api->getChallengeFeed($name, $cursor);
|
||||
if ($feed->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('tag'), new FeedTemplate('Tag', $feed));
|
||||
$api = Wrappers::api();
|
||||
$hashtag = $api->hashtag($name);
|
||||
$hashtag->feed($cursor);
|
||||
if ($hashtag->ok()) {
|
||||
$data = $hashtag->getFull();
|
||||
Wrappers::latte('tag', new FullTemplate($data->info->detail->title, $data));
|
||||
} else {
|
||||
ErrorHandler::show($feed->meta);
|
||||
ErrorHandler::showMeta($hashtag->error());
|
||||
}
|
||||
}
|
||||
|
||||
static public function rss(string $name) {
|
||||
$api = Misc::api();
|
||||
$feed = $api->getChallengeFeed($name);
|
||||
if ($feed->meta->success) {
|
||||
$feed = RSS::build("/tag/{$name}", "{$name} Tag", $feed->info->detail->challenge->desc, $feed->items);
|
||||
// Setup headers
|
||||
RSS::setHeaders('tag.rss');
|
||||
echo $feed;
|
||||
$api = Wrappers::api();
|
||||
$hashtag = $api->hashtag($name);
|
||||
$hashtag->feed();
|
||||
if ($hashtag->ok()) {
|
||||
$data = $hashtag->getFull();
|
||||
Misc::rss($name);
|
||||
Wrappers::latte('rss', new RSSTemplate($name, $data->info->detail->desc, UrlBuilder::tag($name), $data->feed->items));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,29 +4,34 @@ namespace App\Controllers;
|
||||
use App\Helpers\Misc;
|
||||
use App\Models\FeedTemplate;
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\RSS;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\RSSTemplate;
|
||||
|
||||
class TrendingController {
|
||||
static public function get() {
|
||||
$cursor = Misc::getCursor();
|
||||
$api = Misc::api();
|
||||
$feed = $api->getTrendingFeed($cursor);
|
||||
if ($feed->meta->success) {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('trending'), new FeedTemplate('Trending', $feed));
|
||||
$api = Wrappers::api();
|
||||
$cursor = Misc::getTtwid();
|
||||
|
||||
$trending = $api->trending();
|
||||
$trending->feed($cursor);
|
||||
|
||||
$feed = $trending->getFeed();
|
||||
if ($feed && $feed->meta->success) {
|
||||
Wrappers::latte('trending', new FeedTemplate('Trending', $feed));
|
||||
} else {
|
||||
ErrorHandler::show($feed->meta);
|
||||
ErrorHandler::showMeta($trending->error());
|
||||
}
|
||||
}
|
||||
|
||||
static public function rss() {
|
||||
$api = Misc::api();
|
||||
$feed = $api->getTrendingFeed();
|
||||
if ($feed->meta->success) {
|
||||
$feed = RSS::build('/trending', 'Trending', 'Tiktok trending', $feed->items);
|
||||
// Setup headers
|
||||
RSS::setHeaders('trending.rss');
|
||||
echo $feed;
|
||||
$api = Wrappers::api();
|
||||
$trending = $api->trending();
|
||||
$trending->feed();
|
||||
|
||||
$feed = $trending->getFeed();
|
||||
if ($feed && $feed->meta->success) {
|
||||
Misc::rss('Trending');
|
||||
Wrappers::latte('rss', new RSSTemplate('Trending', 'Trending on TikTok', Misc::url('/trending'), $feed->items));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,34 +3,50 @@ namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Helpers\RSS;
|
||||
use App\Models\FeedTemplate;
|
||||
use App\Helpers\UrlBuilder;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\FullTemplate;
|
||||
use App\Models\RSSTemplate;
|
||||
use App\Models\VideoTemplate;
|
||||
|
||||
class UserController {
|
||||
static public function get(string $username) {
|
||||
$cursor = Misc::getCursor();
|
||||
$api = Misc::api();
|
||||
$feed = $api->getUserFeed($username, $cursor);
|
||||
if ($feed->meta->success) {
|
||||
if ($feed->info->detail->user->privateAccount) {
|
||||
http_response_code(400);
|
||||
echo 'Private account detected! Not supported';
|
||||
$api = Wrappers::api();
|
||||
$user = $api->user($username);
|
||||
$user->feed($cursor);
|
||||
if ($user->ok()) {
|
||||
$data = $user->getFull();
|
||||
if ($data->info->detail->privateAccount) {
|
||||
ErrorHandler::showText(401, "Private account detected! Not supported");
|
||||
return;
|
||||
}
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('user'), new FeedTemplate($feed->info->detail->user->nickname, $feed));
|
||||
Wrappers::latte('user', new FullTemplate($data->info->detail->nickname, $data));
|
||||
} else {
|
||||
ErrorHandler::show($feed->meta);
|
||||
ErrorHandler::showMeta($user->error());
|
||||
}
|
||||
}
|
||||
|
||||
static public function video(string $username, string $video_id) {
|
||||
$api = Wrappers::api();
|
||||
$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));
|
||||
} else {
|
||||
ErrorHandler::showMeta($video->error());
|
||||
}
|
||||
}
|
||||
|
||||
static public function rss(string $username) {
|
||||
$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);
|
||||
// Setup headers
|
||||
RSS::setHeaders('user.rss');
|
||||
echo $feed;
|
||||
$api = Wrappers::api();
|
||||
$user = $api->user($username);
|
||||
$user->feed();
|
||||
if ($user->ok()) {
|
||||
$data = $user->getFull();
|
||||
Misc::rss($username);
|
||||
Wrappers::latte('rss', new RSSTemplate($username, $data->info->detail->signature, UrlBuilder::user($username), $data->feed->items));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Misc;
|
||||
use App\Models\ItemTemplate;
|
||||
|
||||
class VideoController {
|
||||
static public function get(string $video_id) {
|
||||
$api = Misc::api();
|
||||
$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));
|
||||
} else {
|
||||
ErrorHandler::show($item->meta);
|
||||
}
|
||||
UserController::video('placeholder', $video_id);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-4
@@ -1,20 +1,39 @@
|
||||
<?php
|
||||
namespace App\Helpers;
|
||||
|
||||
class Cookies {
|
||||
const PROXY = ['proxy-host', 'proxy-port', 'proxy-username', 'proxy-password'];
|
||||
use App\Constants\Themes;
|
||||
|
||||
static public function get(string $name): string {
|
||||
class Cookies {
|
||||
static public function get(string $name, string $default_value = ''): string {
|
||||
if (isset($_COOKIE[$name]) && !empty($_COOKIE[$name])) {
|
||||
return $_COOKIE[$name];
|
||||
}
|
||||
return '';
|
||||
return $default_value;
|
||||
}
|
||||
|
||||
static public function theme(): string {
|
||||
$theme = self::get('theme');
|
||||
$ref = new \ReflectionClass(Themes::class);
|
||||
$themes = $ref->getConstants();
|
||||
if ($theme && in_array($theme, $themes)) {
|
||||
return $theme;
|
||||
}
|
||||
return 'default';
|
||||
}
|
||||
|
||||
static public function downloader(): string {
|
||||
$downloader = self::get('api-downloader', 'default');
|
||||
return $downloader;
|
||||
}
|
||||
|
||||
static public function exists(string $name): bool {
|
||||
return isset($_COOKIE[$name]);
|
||||
}
|
||||
|
||||
static public function check(string $name, string $value): bool {
|
||||
return self::exists($name) && $_COOKIE[$name] === $value;
|
||||
}
|
||||
|
||||
static public function set(string $name, string $value) {
|
||||
setcookie($name, $value, time()+60*60*24*30, '/', '', isset($_SERVER['HTTPS']), true);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Models\ErrorTemplate;
|
||||
use TikScraper\Models\Meta;
|
||||
|
||||
class ErrorHandler {
|
||||
static public function show(object $meta) {
|
||||
static public function showMeta(Meta $meta) {
|
||||
http_response_code($meta->http_code);
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('error'), new ErrorTemplate($meta));
|
||||
Wrappers::latte('error', new ErrorTemplate($meta->http_code, $meta->tiktok_msg, $meta->tiktok_code));
|
||||
}
|
||||
|
||||
static public function showText(int $code, string $msg) {
|
||||
http_response_code($code);
|
||||
Wrappers::latte('error', new ErrorTemplate($code, $msg));
|
||||
}
|
||||
}
|
||||
|
||||
+11
-78
@@ -1,101 +1,34 @@
|
||||
<?php
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Cache\JSONCache;
|
||||
use App\Cache\RedisCache;
|
||||
|
||||
class Misc {
|
||||
static public function getCursor(): int {
|
||||
return isset($_GET['cursor']) && is_numeric($_GET['cursor']) ? (int) $_GET['cursor'] : 0;
|
||||
}
|
||||
|
||||
static public function url(string $endpoint = '') {
|
||||
static public function getTtwid(): string {
|
||||
return isset($_GET['cursor']) ? $_GET['cursor'] : '';
|
||||
}
|
||||
|
||||
static public function url(string $endpoint = ''): string {
|
||||
return self::env('APP_URL', '') . $endpoint;
|
||||
}
|
||||
|
||||
static public function env(string $key, string $default_value): string {
|
||||
return isset($_ENV[$key]) && !empty($_ENV[$key]) ? $_ENV[$key] : $default_value;
|
||||
static public function env(string $key, $default_value) {
|
||||
return $_ENV[$key] ?? $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns absolute path for view
|
||||
*/
|
||||
static public function getView(string $template): string {
|
||||
return __DIR__ . "/../../views/{$template}.latte";
|
||||
return __DIR__ . "/../../templates/views/{$template}.latte";
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup of TikTok Api wrapper
|
||||
* Common method for rss feeds
|
||||
*/
|
||||
static public function api(): \Sovit\TikTok\Api {
|
||||
$options = [];
|
||||
$cacheEngine = false;
|
||||
// Proxy config
|
||||
foreach(Cookies::PROXY as $proxy_element) {
|
||||
if (isset($_COOKIE[$proxy_element])) {
|
||||
$options['proxy'][$proxy_element] = $_COOKIE[$proxy_element];
|
||||
}
|
||||
}
|
||||
// Cache config
|
||||
if (isset($_ENV['API_CACHE'])) {
|
||||
switch ($_ENV['API_CACHE']) {
|
||||
case 'json':
|
||||
$cacheEngine = new JSONCache();
|
||||
break;
|
||||
case '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!');
|
||||
}
|
||||
|
||||
if (isset($_ENV['REDIS_URL'])) {
|
||||
$url = parse_url($_ENV['REDIS_URL']);
|
||||
$host = $url['host'];
|
||||
$port = $url['port'];
|
||||
$password = $url['pass'] ?? null;
|
||||
} else {
|
||||
$host = $_ENV['REDIS_HOST'];
|
||||
$port = (int) $_ENV['REDIS_PORT'];
|
||||
$password = isset($_ENV['REDIS_PASSWORD']) ? $_ENV['REDIS_PASSWORD'] : null;
|
||||
}
|
||||
$cacheEngine = new RedisCache($host, $port, $password);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$api = new \Sovit\TikTok\Api($options, $cacheEngine);
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup of Latte template engine
|
||||
*/
|
||||
static public function latte(): \Latte\Engine {
|
||||
$latte = new \Latte\Engine;
|
||||
$cache_path = self::env('LATTE_CACHE', __DIR__ . '/../../cache/latte');
|
||||
$latte->setTempDirectory($cache_path);
|
||||
|
||||
// -- CUSTOM FUNCTIONS -- //
|
||||
// Get URL with optional endpoint
|
||||
$latte->addFunction('path', function (string $endpoint = ''): string {
|
||||
return self::url($endpoint);
|
||||
});
|
||||
// Version being used
|
||||
$latte->addFunction('version', function (): string {
|
||||
return \Composer\InstalledVersions::getVersion('pablouser1/proxitok');
|
||||
});
|
||||
// https://stackoverflow.com/a/36365553
|
||||
$latte->addFunction('number', function (float $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;
|
||||
static public function rss(string $title) {
|
||||
header('Content-Disposition: attachment; filename="' . $title . '.rss' . '"');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
namespace App\Helpers;
|
||||
|
||||
use \FeedWriter\RSS2;
|
||||
use \Sovit\TikTok\Download;
|
||||
|
||||
class RSS {
|
||||
static public function build(string $endpoint, string $title, string $description, array $items): string {
|
||||
$url = Misc::env('APP_URL', '');
|
||||
$download = new Download();
|
||||
$rss = new RSS2();
|
||||
$rss->setTitle($title);
|
||||
$rss->setDescription($description);
|
||||
$rss->setLink($url . $endpoint);
|
||||
$rss->setSelfLink($url . $endpoint . '/rss');
|
||||
foreach ($items as $item) {
|
||||
$item_rss = $rss->createNewItem();
|
||||
$video = $item->video->playAddr;
|
||||
$item_rss->setTitle($item->desc);
|
||||
$item_rss->setDescription($item->desc);
|
||||
$item_rss->setLink($url . '/video/' . $item->id);
|
||||
$item_rss->setDate((int) $item->createTime);
|
||||
$item_rss->setId($item->id, false);
|
||||
$item_rss->addEnclosure($url . '/stream?url=' . urlencode($video), $download->file_size($video), 'video/mp4');
|
||||
$rss->addItem($item_rss);
|
||||
}
|
||||
return $rss->generateFeed();
|
||||
}
|
||||
|
||||
static public function setHeaders (string $filename) {
|
||||
header('Content-Type: application/rss+xml');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace App\Helpers;
|
||||
|
||||
class UrlBuilder {
|
||||
static public function stream(string $url): string {
|
||||
return Misc::url('/stream?url=' . urlencode($url));
|
||||
}
|
||||
|
||||
static public function download(string $url, string $username, string $id, bool $watermark): string {
|
||||
$down_url = Misc::url('/download?url=' . urlencode($url) . '&id=' . $id . '&user=' . $username);
|
||||
if ($watermark) $down_url .= '&watermark=1';
|
||||
return $down_url;
|
||||
}
|
||||
|
||||
static public function user(string $username): string {
|
||||
return Misc::url('/@' . $username);
|
||||
}
|
||||
|
||||
static public function tag(string $tag): string {
|
||||
return Misc::url('/tag/' . $tag);
|
||||
}
|
||||
|
||||
static public function video_internal(string $username, string $id): string {
|
||||
return Misc::url('/@' . $username . "/video/" . $id);
|
||||
}
|
||||
|
||||
static public function video_external(string $username, string $id): string {
|
||||
return "https://www.tiktok.com/@" . $username . "/video/" . $id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Cache\JSONCache;
|
||||
use App\Cache\RedisCache;
|
||||
use App\Constants\CacheMethods;
|
||||
use App\Models\BaseTemplate;
|
||||
|
||||
class Wrappers {
|
||||
/**
|
||||
* Setup of Latte template engine
|
||||
*/
|
||||
static public function latte(string $template, BaseTemplate $base) {
|
||||
$latte = new \Latte\Engine;
|
||||
$cache_path = Misc::env('LATTE_CACHE', __DIR__ . '/../../cache/latte');
|
||||
$latte->setTempDirectory($cache_path);
|
||||
|
||||
// -- CUSTOM FUNCTIONS -- //
|
||||
// Get URL with optional endpoint
|
||||
$latte->addFunction('path', function (string $endpoint = ''): string {
|
||||
return Misc::url($endpoint);
|
||||
});
|
||||
|
||||
// Static assets
|
||||
$latte->addFunction('static', function (string $type, string $file, bool $isVendor = false): string {
|
||||
$endpoint = '';
|
||||
switch ($type) {
|
||||
case 'js':
|
||||
$endpoint .= '/scripts';
|
||||
break;
|
||||
case 'css':
|
||||
$endpoint .= '/styles';
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Invalid static asset type');
|
||||
}
|
||||
|
||||
if ($isVendor) $endpoint .= '/vendor';
|
||||
|
||||
$endpoint .= '/' . $file;
|
||||
|
||||
return Misc::url($endpoint);
|
||||
});
|
||||
|
||||
$latte->addFunction('theme', function(): string {
|
||||
return Cookies::theme();
|
||||
});
|
||||
|
||||
// Version being used
|
||||
$latte->addFunction('version_frontend', function (): string {
|
||||
return \Composer\InstalledVersions::getVersion('pablouser1/proxitok');
|
||||
});
|
||||
$latte->addFunction('version_scraper', function (): string {
|
||||
return \Composer\InstalledVersions::getVersion('pablouser1/tikscraper');
|
||||
});
|
||||
|
||||
// https://stackoverflow.com/a/36365553
|
||||
$latte->addFunction('number', function (float $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;
|
||||
});
|
||||
|
||||
// UrlBuilder
|
||||
$latte->addFunction('url_stream', function (string $url): string {
|
||||
return UrlBuilder::stream($url);
|
||||
});
|
||||
$latte->addFunction('url_user', function (string $username): string {
|
||||
return UrlBuilder::user($username);
|
||||
});
|
||||
$latte->addFunction('url_video_internal', function (string $username, string $id): string {
|
||||
return UrlBuilder::video_internal($username, $id);
|
||||
});
|
||||
$latte->addFunction('url_video_external', function (string $username, string $id): string {
|
||||
return UrlBuilder::video_external($username, $id);
|
||||
});
|
||||
$latte->addFunction('url_download', function (string $url, string $username, string $id, bool $watermark): string {
|
||||
return UrlBuilder::download($url, $username, $id, $watermark);
|
||||
});
|
||||
|
||||
$latte->render(Misc::getView($template), $base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup of TikTok Api wrapper
|
||||
*/
|
||||
static public function api(): \TikScraper\Api {
|
||||
$method = Misc::env('API_SIGNER', '');
|
||||
$url = Misc::env('API_SIGNER_URL', '');
|
||||
if (!$method) {
|
||||
// Legacy support
|
||||
$browser_url = Misc::env('API_BROWSER_URL', '');
|
||||
if ($url) {
|
||||
$method = 'remote';
|
||||
} elseif ($browser_url) {
|
||||
$url = $browser_url;
|
||||
$method = 'browser';
|
||||
}
|
||||
}
|
||||
|
||||
$options = [
|
||||
'use_test_endpoints' => Misc::env('API_TEST_ENDPOINTS', false) || isset($_COOKIE['api-test_endpoints']) && $_COOKIE['api-test_endpoints'] === 'yes',
|
||||
'signer' => [
|
||||
'method' => $method,
|
||||
'url' => $url,
|
||||
'close_when_done' => false
|
||||
]
|
||||
];
|
||||
|
||||
// -- PROXY CONFIG -- //
|
||||
$proxy_host = Misc::env('PROXY_HOST', '');
|
||||
$proxy_port = Misc::env('PROXY_PORT', '');
|
||||
|
||||
if ($proxy_host && $proxy_port) {
|
||||
$options['proxy'] = [
|
||||
'host' => $proxy_host,
|
||||
'port' => $proxy_port,
|
||||
'username' => Misc::env('PROXY_USERNAME', null),
|
||||
'password' => Misc::env('PROXY_PASSWORD', null)
|
||||
];
|
||||
}
|
||||
|
||||
// Cache config
|
||||
$cacheEngine = null;
|
||||
if (isset($_ENV['API_CACHE'])) {
|
||||
switch ($_ENV['API_CACHE']) {
|
||||
case CacheMethods::JSON:
|
||||
$cacheEngine = new JSONCache();
|
||||
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!');
|
||||
}
|
||||
|
||||
if (isset($_ENV['REDIS_URL'])) {
|
||||
$url = parse_url($_ENV['REDIS_URL']);
|
||||
$host = $url['host'];
|
||||
$port = $url['port'];
|
||||
$password = $url['pass'] ?? null;
|
||||
} else {
|
||||
$host = $_ENV['REDIS_HOST'];
|
||||
$port = (int) $_ENV['REDIS_PORT'];
|
||||
$password = isset($_ENV['REDIS_PASSWORD']) ? $_ENV['REDIS_PASSWORD'] : null;
|
||||
}
|
||||
$cacheEngine = new RedisCache($host, $port, $password);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new \TikScraper\Api($options, $cacheEngine);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ namespace App\Models;
|
||||
*/
|
||||
class BaseTemplate {
|
||||
public string $title;
|
||||
public string $version;
|
||||
|
||||
function __construct(string $title) {
|
||||
$this->title = $title;
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
namespace App\Models;
|
||||
|
||||
class ErrorTemplate extends BaseTemplate {
|
||||
public object $error;
|
||||
public int $http_code = 502;
|
||||
public ?int $tiktok_code = -1;
|
||||
public string $msg = '';
|
||||
|
||||
function __construct(object $error) {
|
||||
|
||||
function __construct(int $http_code, string $msg, ?int $tiktok_code = null) {
|
||||
parent::__construct('Error');
|
||||
$this->error = $error;
|
||||
$this->http_code = $http_code;
|
||||
$this->msg = $msg;
|
||||
$this->tiktok_code = $tiktok_code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ namespace App\Models;
|
||||
* Base for templates with a feed
|
||||
*/
|
||||
class FeedTemplate extends BaseTemplate {
|
||||
public object $feed;
|
||||
public object $data;
|
||||
|
||||
function __construct(string $title, object $feed) {
|
||||
parent::__construct($title);
|
||||
$this->feed = $feed;
|
||||
$this->data = (object) [
|
||||
'feed' => $feed
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use TikScraper\Models\Full;
|
||||
|
||||
/**
|
||||
* Base for templates with both info and feed
|
||||
*/
|
||||
class FullTemplate extends BaseTemplate {
|
||||
public Full $data;
|
||||
|
||||
function __construct(string $title, Full $data) {
|
||||
parent::__construct($title);
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Exclusive for /
|
||||
*/
|
||||
class HomeTemplate extends BaseTemplate {
|
||||
public array $forms = [
|
||||
[
|
||||
'title' => 'Search by user',
|
||||
'input' => 'user',
|
||||
'placeholder' => 'Type username'
|
||||
],
|
||||
[
|
||||
'title' => 'Search by video ID',
|
||||
'input' => 'video',
|
||||
'placeholder' => 'Type video ID'
|
||||
],
|
||||
[
|
||||
'title' => 'Search by tag',
|
||||
'input' => 'tag',
|
||||
'placeholder' => 'Type tag'
|
||||
],
|
||||
[
|
||||
'title' => 'Search by music ID',
|
||||
'input' => 'music',
|
||||
'placeholder' => 'Type music'
|
||||
]
|
||||
];
|
||||
|
||||
function __construct() {
|
||||
parent::__construct('Home');
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
class RSSTemplate extends BaseTemplate {
|
||||
public string $desc;
|
||||
public string $link;
|
||||
public array $items;
|
||||
|
||||
function __construct(string $title, string $desc, string $link, array $items) {
|
||||
parent::__construct($title);
|
||||
$this->desc = $desc;
|
||||
$this->link = $link;
|
||||
$this->items = $items;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,33 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use App\Constants\Themes;
|
||||
use App\Helpers\Cookies;
|
||||
use TikScraper\Constants\DownloadMethods;
|
||||
|
||||
/**
|
||||
* Exclusive for /settings
|
||||
*/
|
||||
* Settings model with all possible config items
|
||||
*/
|
||||
class SettingsTemplate extends BaseTemplate {
|
||||
public array $proxy_elements = [];
|
||||
public array $downloaders = [];
|
||||
public array $themes = [];
|
||||
public bool $isTestEndpoints = false;
|
||||
public string $currentDownloader;
|
||||
public string $currentTheme;
|
||||
public bool $isServiceWorker = false;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct('Settings');
|
||||
$this->proxy_elements = Cookies::PROXY;
|
||||
parent::__construct("Settings");
|
||||
// Downloaders list
|
||||
$ref = new \ReflectionClass(DownloadMethods::class);
|
||||
$this->downloaders = $ref->getConstants();
|
||||
// Themes list
|
||||
$ref = new \ReflectionClass(Themes::class);
|
||||
$this->themes = $ref->getConstants();
|
||||
// Cookies data
|
||||
$this->isTestEndpoints = Cookies::check('api-test_endpoints', 'yes');
|
||||
$this->currentDownloader = Cookies::downloader();
|
||||
$this->currentTheme = Cookies::theme();
|
||||
$this->isServiceWorker = Cookies::check('misc-sw', 'yes');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Base for templates with a feed
|
||||
*/
|
||||
class VideoTemplate extends BaseTemplate {
|
||||
public object $item;
|
||||
public object $detail;
|
||||
public string $layout = 'hero';
|
||||
|
||||
function __construct(object $item, object $detail, bool $isEmbed = false) {
|
||||
parent::__construct('Video');
|
||||
$this->item = $item;
|
||||
$this->detail = $detail;
|
||||
if ($isEmbed) {
|
||||
$this->layout = 'embed';
|
||||
} else {
|
||||
$this->layout = 'hero';
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
@@ -1,52 +0,0 @@
|
||||
<link rel="stylesheet" href="{path('/styles/feed.css')}">
|
||||
<noscript>JavaScript is required for this section to work!</noscript>
|
||||
<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="{path('/stream?url=' . urlencode($item->video->playAddr) . '&download=1')}"
|
||||
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>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
<div n:ifset="$feed->info" class="buttons">
|
||||
{if isset($_GET['cursor']) && $_GET['cursor'] != 0}
|
||||
<a class="button is-danger" href="?cursor=0">First</a>
|
||||
<a class="button is-danger" href="?cursor={$feed->minCursor}">Back</a>
|
||||
{/if}
|
||||
<a n:attr="disabled => $feed->hasMore" class="button is-success" href="?cursor={$feed->maxCursor}">Next</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- MODAL -->
|
||||
<div id="modal" class="modal">
|
||||
<div id="modal-background" class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<button id="modal-close" class="delete" aria-label="close"></button>
|
||||
<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>
|
||||
</section>
|
||||
<footer class="modal-card-foot has-text-centered">
|
||||
<div class="container">
|
||||
<a id="download_button" target="_blank" class="button is-info" download>Download</a>
|
||||
<p id="audio_title"></p>
|
||||
<audio id="audio" controls preload="none"></audio>
|
||||
<div class="buttons is-centered">
|
||||
<button id="back-button" class="button is-danger">Back</button>
|
||||
<button id="next-button" class="button is-success">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{path('/scripts/feed.js')}"></script>
|
||||
@@ -1,9 +0,0 @@
|
||||
<div class="tags">
|
||||
{if !empty($following)}
|
||||
{foreach $following as $user}
|
||||
<span class="tag">{$user}</span>
|
||||
{/foreach}
|
||||
{else}
|
||||
<p>None</p>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,8 +0,0 @@
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
Made with <span style="color: #e25555;">♥</span> in <a href="https://github.com/pablouser1/ProxiTok">Github</a>
|
||||
</p>
|
||||
<p>Version: {version()}</p>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -1,10 +0,0 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="ProxiTok" />
|
||||
<meta property="og:description" content="Alternative frontend for TikTok" />
|
||||
<meta property="og:type" content="website" />
|
||||
<link rel="stylesheet" href="{path('/styles/bulma.min.css')}">
|
||||
<title>{$title} - ProxiTok</title>
|
||||
</head>
|
||||
@@ -1,18 +0,0 @@
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a role="button" id="navbar-burger" class="navbar-burger" aria-label="menu" aria-expanded="false">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbar-menu" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a href="{path('/')}" class="navbar-item">Home</a>
|
||||
<a href="{path('/settings')}" class="navbar-item">Settings</a>
|
||||
<a href="{path('/about')}" class="navbar-item">About</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<script src="{path('/scripts/navbar.js')}"></script>
|
||||
@@ -1,15 +0,0 @@
|
||||
<form action="{path('/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>
|
||||
+12
-12
@@ -1,30 +1,30 @@
|
||||
{
|
||||
"name": "pablouser1/proxitok",
|
||||
"description": "An alternative frontend for TikTok",
|
||||
"version": "1.4.1",
|
||||
"version": "2.4.3.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"type": "project",
|
||||
"homepage": "https://github.com/pablouser1/ProxiTok",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Pablo Ferreiro",
|
||||
"homepage": "https://github.com/pablouser1"
|
||||
}
|
||||
],
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/pablouser1/TikTok-API-PHP"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/pablouser1/ProxiTok",
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"platform": {
|
||||
"php": "7.4"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4|^8.0",
|
||||
"ext-redis": "^5.3.2",
|
||||
"ext-mbstring": "*",
|
||||
"ssovit/tiktok-api": "dev-rework",
|
||||
"latte/latte": "^2.10",
|
||||
"vlucas/phpdotenv": "^5.4",
|
||||
"latte/latte": "^2.11",
|
||||
"bramus/router": "^1.6",
|
||||
"mibe/feedwriter": "^1.1"
|
||||
"josegonzalez/dotenv": "dev-master",
|
||||
"pablouser1/tikscraper": "^2.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
Generated
+241
-360
@@ -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": "b40da817e3b81d14a1ed55aad52cf434",
|
||||
"content-hash": "7dc0bdf9fcecc4c3a73fb2fb1eece6f4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bramus/router",
|
||||
@@ -58,30 +58,36 @@
|
||||
"time": "2021-11-18T19:24:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "graham-campbell/result-type",
|
||||
"version": "v1.0.4",
|
||||
"name": "josegonzalez/dotenv",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/GrahamCampbell/Result-Type.git",
|
||||
"reference": "0690bde05318336c7221785f2a932467f98b64ca"
|
||||
"url": "https://github.com/josegonzalez/php-dotenv.git",
|
||||
"reference": "f6d2fb63610f98b7ae859031566228dbade1a79c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca",
|
||||
"reference": "0690bde05318336c7221785f2a932467f98b64ca",
|
||||
"url": "https://api.github.com/repos/josegonzalez/php-dotenv/zipball/f6d2fb63610f98b7ae859031566228dbade1a79c",
|
||||
"reference": "f6d2fb63610f98b7ae859031566228dbade1a79c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"phpoption/phpoption": "^1.8"
|
||||
"m1/env": "2.*",
|
||||
"php": ">=5.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8"
|
||||
"php-mock/php-mock-phpunit": "^1.1",
|
||||
"satooshi/php-coveralls": "1.*",
|
||||
"squizlabs/php_codesniffer": "2.*"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GrahamCampbell\\ResultType\\": "src/"
|
||||
"psr-0": {
|
||||
"josegonzalez\\Dotenv": [
|
||||
"src",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -90,62 +96,52 @@
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
"name": "Jose Diaz-Gonzalez",
|
||||
"email": "dotenv@josegonzalez.com",
|
||||
"homepage": "http://josediazgonzalez.com",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "An Implementation Of The Result Type",
|
||||
"description": "dotenv file parsing for PHP",
|
||||
"homepage": "https://github.com/josegonzalez/php-dotenv",
|
||||
"keywords": [
|
||||
"Graham Campbell",
|
||||
"GrahamCampbell",
|
||||
"Result Type",
|
||||
"Result-Type",
|
||||
"result"
|
||||
"configuration",
|
||||
"dotenv",
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
|
||||
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4"
|
||||
"issues": "https://github.com/josegonzalez/php-dotenv/issues",
|
||||
"source": "https://github.com/josegonzalez/php-dotenv/tree/master"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-21T21:41:47+00:00"
|
||||
"time": "2019-07-07T21:21:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "latte/latte",
|
||||
"version": "v2.10.8",
|
||||
"version": "v2.11.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/latte.git",
|
||||
"reference": "596b28bf098ebb852732d60b00538139a009c4db"
|
||||
"reference": "af4dbb23a6044b1cf4904830a9b00246318c93d4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/latte/zipball/596b28bf098ebb852732d60b00538139a009c4db",
|
||||
"reference": "596b28bf098ebb852732d60b00538139a009c4db",
|
||||
"url": "https://api.github.com/repos/nette/latte/zipball/af4dbb23a6044b1cf4904830a9b00246318c93d4",
|
||||
"reference": "af4dbb23a6044b1cf4904830a9b00246318c93d4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"php": ">=7.1 <8.2"
|
||||
"php": ">=7.1 <8.3"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/application": "<2.4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/php-generator": "^3.3.4",
|
||||
"nette/tester": "~2.0",
|
||||
"nette/tester": "^2.0",
|
||||
"nette/utils": "^3.0",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"phpstan/phpstan": "^1",
|
||||
"tracy/tracy": "^2.3"
|
||||
},
|
||||
"suggest": {
|
||||
@@ -161,7 +157,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.10-dev"
|
||||
"dev-master": "2.11-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -199,274 +195,102 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/latte/issues",
|
||||
"source": "https://github.com/nette/latte/tree/v2.10.8"
|
||||
"source": "https://github.com/nette/latte/tree/v2.11.6"
|
||||
},
|
||||
"time": "2022-01-04T14:13:28+00:00"
|
||||
"time": "2022-11-04T14:53:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mibe/feedwriter",
|
||||
"version": "v1.1.1",
|
||||
"name": "m1/env",
|
||||
"version": "2.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mibe/FeedWriter.git",
|
||||
"reference": "f4cc748ad8700e36663f08cfeebe7fd39b00eea2"
|
||||
"url": "https://github.com/m1/Env.git",
|
||||
"reference": "5c296e3e13450a207e12b343f3af1d7ab569f6f3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mibe/FeedWriter/zipball/f4cc748ad8700e36663f08cfeebe7fd39b00eea2",
|
||||
"reference": "f4cc748ad8700e36663f08cfeebe7fd39b00eea2",
|
||||
"url": "https://api.github.com/repos/m1/Env/zipball/5c296e3e13450a207e12b343f3af1d7ab569f6f3",
|
||||
"reference": "5c296e3e13450a207e12b343f3af1d7ab569f6f3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FeedWriter\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Bemmerl",
|
||||
"email": "mail@mx-server.de"
|
||||
},
|
||||
{
|
||||
"name": "Phil Freo"
|
||||
},
|
||||
{
|
||||
"name": "Paul Ferrett"
|
||||
},
|
||||
{
|
||||
"name": "Brennen Bearnes"
|
||||
},
|
||||
{
|
||||
"name": "Michael Robinson",
|
||||
"email": "mike@pagesofinterest.net"
|
||||
},
|
||||
{
|
||||
"name": "Baptiste Fontaine"
|
||||
},
|
||||
{
|
||||
"name": "Kristián Valentín"
|
||||
},
|
||||
{
|
||||
"name": "Brandtley McMinn"
|
||||
},
|
||||
{
|
||||
"name": "Julian Bogdani"
|
||||
},
|
||||
{
|
||||
"name": "Anis Uddin Ahmad",
|
||||
"email": "anis.programmer@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Cedric Gampert"
|
||||
},
|
||||
{
|
||||
"name": "Yamek"
|
||||
},
|
||||
{
|
||||
"name": "thielj"
|
||||
},
|
||||
{
|
||||
"name": "Pavel Khakhlou"
|
||||
},
|
||||
{
|
||||
"name": "Daniel"
|
||||
},
|
||||
{
|
||||
"name": "Tino Goratsch"
|
||||
}
|
||||
],
|
||||
"description": "Generate feeds in either RSS 1.0, RSS 2.0 or ATOM formats",
|
||||
"homepage": "https://github.com/mibe/FeedWriter",
|
||||
"keywords": [
|
||||
"RSS 1.0",
|
||||
"atom",
|
||||
"feed",
|
||||
"rss",
|
||||
"rss 2.0",
|
||||
"rss2"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mibe/FeedWriter/issues",
|
||||
"source": "https://github.com/mibe/FeedWriter/tree/master"
|
||||
},
|
||||
"time": "2016-11-19T20:47:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/schmittjoh/php-option.git",
|
||||
"reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15",
|
||||
"reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.4.1",
|
||||
"phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8"
|
||||
"phpunit/phpunit": "4.*",
|
||||
"scrutinizer/ocular": "~1.1",
|
||||
"squizlabs/php_codesniffer": "^2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"josegonzalez/dotenv": "For loading of .env",
|
||||
"m1/vars": "For loading of configs"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOption\\": "src/PhpOption/"
|
||||
"M1\\Env\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Johannes M. Schmitt",
|
||||
"email": "schmittjoh@gmail.com",
|
||||
"homepage": "https://github.com/schmittjoh"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
}
|
||||
],
|
||||
"description": "Option Type for PHP",
|
||||
"keywords": [
|
||||
"language",
|
||||
"option",
|
||||
"php",
|
||||
"type"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/schmittjoh/php-option/issues",
|
||||
"source": "https://github.com/schmittjoh/php-option/tree/1.8.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"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": "c6667d70757fd5405ee1b51eaa6602d8758d2abd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pablouser1/TikTok-API-PHP/zipball/c6667d70757fd5405ee1b51eaa6602d8758d2abd",
|
||||
"reference": "c6667d70757fd5405ee1b51eaa6602d8758d2abd",
|
||||
"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/",
|
||||
"name": "Miles Croxford",
|
||||
"email": "hello@milescroxford.com",
|
||||
"homepage": "http://milescroxford.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Unofficial TikTok API for PHP",
|
||||
"homepage": "https://github.com/ssovit/TikTok-API-PHP",
|
||||
"description": "Env is a lightweight library bringing .env file parser compatibility to PHP. In short - it enables you to read .env files with PHP.",
|
||||
"homepage": "https://github.com/m1/Env",
|
||||
"keywords": [
|
||||
"tiktok",
|
||||
"tiktok-api",
|
||||
"tiktok-scraper"
|
||||
".env",
|
||||
"config",
|
||||
"dotenv",
|
||||
"env",
|
||||
"loader",
|
||||
"m1",
|
||||
"parser",
|
||||
"support"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/ssovit/TikTok-API-PHP",
|
||||
"issues": "https://github.com/ssovit/TikTok-API-PHP/issues",
|
||||
"email": "sovit.tamrakar@gmail.com"
|
||||
"issues": "https://github.com/m1/Env/issues",
|
||||
"source": "https://github.com/m1/Env/tree/2.2.0"
|
||||
},
|
||||
"time": "2022-02-06T13:17:16+00:00"
|
||||
"time": "2020-02-19T09:02:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.24.0",
|
||||
"name": "pablouser1/tikscraper",
|
||||
"version": "v2.3.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
|
||||
"url": "https://github.com/pablouser1/TikScraperPHP.git",
|
||||
"reference": "b67340e62cda1fb117b518e4f812e327aec094d6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"url": "https://api.github.com/repos/pablouser1/TikScraperPHP/zipball/b67340e62cda1fb117b518e4f812e327aec094d6",
|
||||
"reference": "b67340e62cda1fb117b518e4f812e327aec094d6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-xml": "*",
|
||||
"php": ">=7.4|^8.0",
|
||||
"php-webdriver/webdriver": "^1.12",
|
||||
"sapistudio/seleniumstealth": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
"require-dev": {
|
||||
"pestphp/pest": "^1.22"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
"TikScraper\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -474,53 +298,124 @@
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
"name": "Pablo Ferreiro",
|
||||
"homepage": "https://pabloferreiro.es"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"description": "Get data from TikTok API",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0"
|
||||
"issues": "https://github.com/pablouser1/TikScraperPHP/issues",
|
||||
"source": "https://github.com/pablouser1/TikScraperPHP/tree/v2.3.2.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
"time": "2022-11-04T18:45:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.24.0",
|
||||
"name": "php-webdriver/webdriver",
|
||||
"version": "1.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
|
||||
"url": "https://github.com/php-webdriver/php-webdriver.git",
|
||||
"reference": "6dfe5f814b796c1b5748850aa19f781b9274c36c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/6dfe5f814b796c1b5748850aa19f781b9274c36c",
|
||||
"reference": "6dfe5f814b796c1b5748850aa19f781b9274c36c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zip": "*",
|
||||
"php": "^5.6 || ~7.0 || ^8.0",
|
||||
"symfony/polyfill-mbstring": "^1.12",
|
||||
"symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0 || ^6.0"
|
||||
},
|
||||
"replace": {
|
||||
"facebook/webdriver": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ondram/ci-detector": "^2.1 || ^3.5 || ^4.0",
|
||||
"php-coveralls/php-coveralls": "^2.4",
|
||||
"php-mock/php-mock-phpunit": "^1.1 || ^2.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/var-dumper": "^3.3 || ^4.0 || ^5.0 || ^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-SimpleXML": "For Firefox profile creation"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/Exception/TimeoutException.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Facebook\\WebDriver\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.",
|
||||
"homepage": "https://github.com/php-webdriver/php-webdriver",
|
||||
"keywords": [
|
||||
"Chromedriver",
|
||||
"geckodriver",
|
||||
"php",
|
||||
"selenium",
|
||||
"webdriver"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-webdriver/php-webdriver/issues",
|
||||
"source": "https://github.com/php-webdriver/php-webdriver/tree/1.13.1"
|
||||
},
|
||||
"time": "2022-10-11T11:49:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sapistudio/seleniumstealth",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Sapistudio/SeleniumStealth.git",
|
||||
"reference": "4b392077e3d609fed564b78ae2e8becb98f01957"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Sapistudio/SeleniumStealth/zipball/4b392077e3d609fed564b78ae2e8becb98f01957",
|
||||
"reference": "4b392077e3d609fed564b78ae2e8becb98f01957",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SapiStudio\\SeleniumStealth\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Sapistudio/SeleniumStealth/issues",
|
||||
"source": "https://github.com/Sapistudio/SeleniumStealth/tree/1.0.3"
|
||||
},
|
||||
"time": "2022-01-10T20:04:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.26.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -535,7 +430,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
"dev-main": "1.26-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -543,12 +438,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -574,7 +469,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -590,20 +485,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-30T18:21:41+00:00"
|
||||
"time": "2022-05-24T11:49:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.24.0",
|
||||
"version": "v1.26.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "57b712b08eddb97c762a8caa32c84e037892d2e9"
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9",
|
||||
"reference": "57b712b08eddb97c762a8caa32c84e037892d2e9",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -612,7 +507,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
"dev-main": "1.26-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -657,7 +552,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -673,101 +568,87 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-13T13:58:33+00:00"
|
||||
"time": "2022-05-10T07:21:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
"version": "v5.4.1",
|
||||
"name": "symfony/process",
|
||||
"version": "v5.4.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||
"reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f"
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f",
|
||||
"reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1",
|
||||
"reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pcre": "*",
|
||||
"graham-campbell/result-type": "^1.0.2",
|
||||
"php": "^7.1.3 || ^8.0",
|
||||
"phpoption/phpoption": "^1.8",
|
||||
"symfony/polyfill-ctype": "^1.23",
|
||||
"symfony/polyfill-mbstring": "^1.23.1",
|
||||
"symfony/polyfill-php80": "^1.23.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.4.1",
|
||||
"ext-filter": "*",
|
||||
"phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-filter": "Required to use the boolean validator."
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dotenv\\": "src/"
|
||||
}
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Vance Lucas",
|
||||
"email": "vance@vancelucas.com",
|
||||
"homepage": "https://github.com/vlucas"
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||
"keywords": [
|
||||
"dotenv",
|
||||
"env",
|
||||
"environment"
|
||||
],
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||
"source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1"
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-12-12T23:22:04+00:00"
|
||||
"time": "2022-06-27T16:58:25+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"ssovit/tiktok-api": 20
|
||||
"josegonzalez/dotenv": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.4|^8.0",
|
||||
"ext-redis": "^5.3.2",
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.2.0"
|
||||
"platform-overrides": {
|
||||
"php": "7.4"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
web:
|
||||
container_name: proxitok-web
|
||||
image: ghcr.io/pablouser1/proxitok:master
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
- LATTE_CACHE=/cache
|
||||
- API_CACHE=redis
|
||||
- REDIS_HOST=proxitok-redis
|
||||
- REDIS_PORT=6379
|
||||
- API_SIGNER=remote
|
||||
- API_SIGNER_URL=http://proxitok-signer:8080/signature
|
||||
volumes:
|
||||
- proxitok-cache:/cache
|
||||
depends_on:
|
||||
- redis
|
||||
- signer
|
||||
redis:
|
||||
container_name: proxitok-redis
|
||||
image: redis:7-alpine
|
||||
command: redis-server --save 60 1 --loglevel warning
|
||||
restart: unless-stopped
|
||||
signer:
|
||||
container_name: proxitok-signer
|
||||
image: ghcr.io/pablouser1/signtok:master
|
||||
volumes:
|
||||
proxitok-cache:
|
||||
@@ -0,0 +1,15 @@
|
||||
// Workaround to allow TikTok embed
|
||||
|
||||
const blockquotes = document.getElementsByClassName('tiktok-embed')
|
||||
for (let i = 0; i < blockquotes.length; i++) {
|
||||
const blockquote = blockquotes[i]
|
||||
if (blockquote.children.length > 0 && blockquote.children[0].tagName !== 'iframe') {
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.style = 'width: 100%; height: 710px; display: block; visibility: unset; max-height: 710px;'
|
||||
iframe.src = 'https://www.tiktok.com/embed/v2/' + blockquote.dataset.videoId // This url will get redirected
|
||||
// Remove placeholder section
|
||||
blockquote.children[0].remove()
|
||||
// Add iframe
|
||||
blockquote.appendChild(iframe)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 626 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -1,9 +1,12 @@
|
||||
<?php
|
||||
require __DIR__ . "/vendor/autoload.php";
|
||||
|
||||
// LOAD DOTENV
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||
$dotenv->safeLoad();
|
||||
$dotenv = new josegonzalez\Dotenv\Loader(__DIR__ . '/.env');
|
||||
$dotenv->raiseExceptions(false);
|
||||
$result = $dotenv->parse();
|
||||
if ($result !== false) {
|
||||
$dotenv->toEnv();
|
||||
}
|
||||
|
||||
// ROUTER
|
||||
$router = new Bramus\Router\Router();
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
+27
-9
@@ -1,19 +1,28 @@
|
||||
<?php
|
||||
use App\Helpers\Misc;
|
||||
/** @var \Bramus\Router\Router $router */
|
||||
|
||||
use App\Helpers\ErrorHandler;
|
||||
use App\Helpers\Wrappers;
|
||||
use App\Models\BaseTemplate;
|
||||
use App\Models\HomeTemplate;
|
||||
|
||||
$router->set404(function () {
|
||||
ErrorHandler::showText(404, "That endpoint doesn't exist");
|
||||
});
|
||||
|
||||
$router->get('/', function () {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('home'), new HomeTemplate);
|
||||
Wrappers::latte('home', new BaseTemplate('Home'));
|
||||
});
|
||||
|
||||
$router->get('/about', function () {
|
||||
$latte = Misc::latte();
|
||||
$latte->render(Misc::getView('about'), new BaseTemplate('About'));
|
||||
Wrappers::latte('about', new BaseTemplate('About'));
|
||||
});
|
||||
|
||||
$router->get('/verify', function () {
|
||||
Wrappers::latte('verify', new BaseTemplate('verify'));
|
||||
});
|
||||
|
||||
$router->get('/stream', 'ProxyController@stream');
|
||||
$router->get('/download', 'ProxyController@download');
|
||||
$router->get('/redirect', 'RedirectController@redirect');
|
||||
|
||||
$router->mount('/trending', function () use ($router) {
|
||||
@@ -23,13 +32,17 @@ $router->mount('/trending', function () use ($router) {
|
||||
|
||||
$router->mount('/@([^/]+)', function () use ($router) {
|
||||
$router->get('/', 'UserController@get');
|
||||
$router->get('/video/(\w+)', 'UserController@video');
|
||||
$router->get('/rss', 'UserController@rss');
|
||||
});
|
||||
|
||||
// Deprecated, please use /@USERNAME/video/VIDEO_ID instead
|
||||
$router->get('/video/(\w+)', 'VideoController@get');
|
||||
// Workaround that allows /t endpoints
|
||||
$router->get('/t/([^/]+)', 'VideoController@get');
|
||||
|
||||
$router->mount('/tag', function () use ($router) {
|
||||
$router->get('/([^/]+)', 'TagController@get');
|
||||
$router->mount('/tag/([^/]+)', function () use ($router) {
|
||||
$router->get('/', 'TagController@get');
|
||||
$router->get('/rss', 'TagController@rss');
|
||||
});
|
||||
|
||||
@@ -38,7 +51,12 @@ $router->get('/music/([^/]+)', 'MusicController@get');
|
||||
// -- Settings -- //
|
||||
$router->mount('/settings', function () use ($router) {
|
||||
$router->get('/', 'SettingsController@index');
|
||||
$router->post('/proxy', 'SettingsController@proxy');
|
||||
$router->post('/general', 'SettingsController@general');
|
||||
$router->post('/api', 'SettingsController@api');
|
||||
$router->post('/misc', 'SettingsController@misc');
|
||||
});
|
||||
|
||||
$router->get('/discover', 'DiscoverController@get');
|
||||
|
||||
// -- EMBED -- //
|
||||
$router->get('/embed/v2/(\d+)', 'EmbedController@v2');
|
||||
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
const video = document.getElementById('video')
|
||||
const item_title = document.getElementById('item_title')
|
||||
const audio = document.getElementById('audio')
|
||||
const audio_title = document.getElementById('audio_title')
|
||||
const modal = document.getElementById('modal')
|
||||
const download_button = document.getElementById('download_button')
|
||||
|
||||
// -- HELPERS -- //
|
||||
const getHash = () => window.location.hash.substring(1)
|
||||
|
||||
const getVideoDataById = (id) => {
|
||||
const el = document.getElementById(id)
|
||||
if (el) {
|
||||
return el.dataset
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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')
|
||||
|
||||
// -- MODAL -- //
|
||||
const swapData = ({ video_url, desc, video_download, music_title, music_url }) => {
|
||||
video.src = video_url
|
||||
item_title.innerText = desc
|
||||
download_button.href = video_download
|
||||
audio_title.innerText = music_title
|
||||
audio.src = music_url
|
||||
}
|
||||
|
||||
const showModal = (id) => {
|
||||
const dataset = getVideoDataById(id)
|
||||
if (dataset) {
|
||||
swapData(dataset)
|
||||
modal.classList.toggle('is-active')
|
||||
video.play()
|
||||
}
|
||||
}
|
||||
|
||||
const hideModel = () => {
|
||||
video.pause()
|
||||
audio.pause()
|
||||
video.currentTime = 0
|
||||
modal.classList.toggle('is-active')
|
||||
toggleButton('back-button', false)
|
||||
toggleButton('next-button', false)
|
||||
history.pushState('', document.title, window.location.pathname + window.location.search)
|
||||
}
|
||||
|
||||
const getPrevOrNext = (forward) => {
|
||||
const hash = getHash()
|
||||
if (hash) {
|
||||
const el = document.getElementById(hash)
|
||||
if (el) {
|
||||
if (forward) {
|
||||
return el.parentElement.nextElementSibling ? el.parentElement.nextElementSibling.children[0] : null
|
||||
}
|
||||
return el.parentElement.previousElementSibling ? el.parentElement.previousElementSibling.children[0] : null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
// Max reached, disable buttons depending on direction
|
||||
if (forward) {
|
||||
toggleButton('next-button', true)
|
||||
} else {
|
||||
toggleButton('back-button', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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]
|
||||
}
|
||||
|
||||
if (img && gif) {
|
||||
if (!gif.src) {
|
||||
gif.src = gif.dataset.src
|
||||
}
|
||||
img.classList.toggle('hidden')
|
||||
gif.classList.toggle('hidden')
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
hashChange()
|
||||
@@ -0,0 +1,127 @@
|
||||
var opened_video_id = null
|
||||
|
||||
const video = document.getElementById('video')
|
||||
const item_title = document.getElementById('item_title')
|
||||
const item_date = document.getElementById('item_date')
|
||||
const audio = document.getElementById('audio')
|
||||
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')
|
||||
|
||||
const getVideoDataById = id => {
|
||||
const el = document.getElementById(id)
|
||||
if (el) {
|
||||
opened_video_id = id
|
||||
return el.dataset
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const isModalActive = () => modal.classList.contains('is-active')
|
||||
|
||||
const toggleButton = (id, force) => document.getElementById(id).toggleAttribute('disabled', force)
|
||||
|
||||
// -- MODAL -- //
|
||||
const swapData = ({ video_url, desc, createtime, video_download_watermark, video_download_nowatermark, video_share_url, music_title, music_url }) => {
|
||||
video.src = video_url
|
||||
item_title.innerText = desc
|
||||
item_date.innerText = new Date(createtime * 1000).toLocaleString()
|
||||
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 dataset = getVideoDataById(id)
|
||||
if (dataset) {
|
||||
swapData(dataset)
|
||||
modal.classList.toggle('is-active')
|
||||
video.play()
|
||||
}
|
||||
}
|
||||
|
||||
const hideModel = () => {
|
||||
video.pause()
|
||||
audio.pause()
|
||||
video.currentTime = 0
|
||||
modal.classList.toggle('is-active')
|
||||
toggleButton('back-button', false)
|
||||
toggleButton('next-button', false)
|
||||
history.pushState('', document.title, window.location.pathname + window.location.search)
|
||||
}
|
||||
|
||||
const getPrevOrNext = forward => {
|
||||
if (opened_video_id) {
|
||||
const el = document.getElementById(opened_video_id)
|
||||
if (el) {
|
||||
if (forward) {
|
||||
return el.nextElementSibling
|
||||
}
|
||||
return el.previousElementSibling
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const moveVideo = forward => {
|
||||
// Re-enable buttons
|
||||
toggleButton('back-button', false)
|
||||
toggleButton('next-button', false)
|
||||
const new_el = getPrevOrNext(forward)
|
||||
if (new_el) {
|
||||
opened_video_id = new_el.id
|
||||
swapData(new_el.dataset)
|
||||
} else {
|
||||
// Max reached, disable buttons depending on direction
|
||||
if (forward) {
|
||||
toggleButton('next-button', true)
|
||||
} else {
|
||||
toggleButton('back-button', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EVENTS //
|
||||
const openVideo = video_id => {
|
||||
if (isModalActive()) {
|
||||
const dataset = getVideoDataById(video_id)
|
||||
if (dataset) {
|
||||
swapData(dataset)
|
||||
}
|
||||
} else {
|
||||
showModal(video_id)
|
||||
}
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
// Image hover
|
||||
const images = document.getElementsByClassName("clickable-img")
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
images[i].addEventListener('mouseenter', swapImages, false)
|
||||
images[i].addEventListener('mouseout', swapImages, false)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
This facilities making the compressed bulma.min.css and cssgg.min.css files, install all dependencies using:
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
And then use:
|
||||
```
|
||||
yarn bulma && yarn cssgg
|
||||
```
|
||||
|
||||
This compiles bulma.scss and cssgg.scss to a css file and helps lowering the size of the css that will be sent to the user.
|
||||
+7
-2
@@ -10,9 +10,12 @@ $bulmaswatch-import-font: false;
|
||||
@import "./node_modules/bulma/sass/base/_all.sass";
|
||||
|
||||
// Elements
|
||||
@import "./node_modules/bulma/sass/elements/button.sass";
|
||||
@import "./node_modules/bulma/sass/elements/box.sass";
|
||||
@import "./node_modules/bulma/sass/elements/button.sass";
|
||||
@import "./node_modules/bulma/sass/elements/container.sass";
|
||||
@import "./node_modules/bulma/sass/elements/content.sass";
|
||||
@import "./node_modules/bulma/sass/elements/icon.sass";
|
||||
@import "./node_modules/bulma/sass/elements/image.sass";
|
||||
@import "./node_modules/bulma/sass/elements/other.sass";
|
||||
@import "./node_modules/bulma/sass/elements/tag.sass";
|
||||
@import "./node_modules/bulma/sass/elements/title.sass";
|
||||
@@ -21,13 +24,15 @@ $bulmaswatch-import-font: false;
|
||||
@import "./node_modules/bulma/sass/form/shared.sass";
|
||||
@import "./node_modules/bulma/sass/form/input-textarea.sass";
|
||||
@import "./node_modules/bulma/sass/form/checkbox-radio.sass";
|
||||
@import "./node_modules/bulma/sass/form/select.sass";
|
||||
@import "./node_modules/bulma/sass/form/tools.sass";
|
||||
|
||||
// Components
|
||||
@import "./node_modules/bulma/sass/components/card.sass";
|
||||
@import "./node_modules/bulma/sass/components/dropdown.sass";
|
||||
@import "./node_modules/bulma/sass/components/media.sass";
|
||||
@import "./node_modules/bulma/sass/components/modal.sass";
|
||||
@import "./node_modules/bulma/sass/components/navbar.sass";
|
||||
@import "./node_modules/bulma/sass/components/media.sass";
|
||||
|
||||
// Grids
|
||||
@import "./node_modules/bulma/sass/grid/columns.sass";
|
||||
@@ -0,0 +1,23 @@
|
||||
@charset "utf-8";
|
||||
// Common
|
||||
@import "./node_modules/css.gg/icons/scss/home.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/options.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/info.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/code-slash.scss";
|
||||
|
||||
// Home
|
||||
@import "./node_modules/css.gg/icons/scss/search.scss";
|
||||
|
||||
// Feed
|
||||
@import "./node_modules/css.gg/icons/scss/feed.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/eye.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/heart.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/comment.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/share.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/software-download.scss";
|
||||
|
||||
@import "./node_modules/css.gg/icons/scss/lock.scss";
|
||||
@import "./node_modules/css.gg/icons/scss/lock-unlock.scss";
|
||||
|
||||
// Video
|
||||
@import "./node_modules/css.gg/icons/scss/music.scss";
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "proxitok-scss",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bulma": "sass --style=compressed bulma.scss ../styles/vendor/bulma.min.css",
|
||||
"cssgg": "sass --style=compressed cssgg.scss ../styles/vendor/cssgg.min.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"bulma": "^0.9.3",
|
||||
"bulmaswatch": "^0.8.1",
|
||||
"css.gg": "^2.0.0",
|
||||
"sass": "^1.46.0"
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,9 @@ braces@~3.0.2:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
bulma@^0.9.3:
|
||||
version "0.9.3"
|
||||
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.9.3.tgz#ddccb7436ebe3e21bf47afe01d3c43a296b70243"
|
||||
integrity sha512-0d7GNW1PY4ud8TWxdNcP6Cc8Bu7MxcntD/RRLGWuiw/s0a9P+XlH/6QoOIrmbj6o8WWJzJYhytiu9nFjTszk1g==
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.9.4.tgz#0ca8aeb1847a34264768dba26a064c8be72674a1"
|
||||
integrity sha512-86FlT5+1GrsgKbPLRRY7cGDg8fsJiP/jzTqXXVqiUZZ2aZT8uemEOHlU1CDU+TxklPEZ11HZNNWclRBBecP4CQ==
|
||||
|
||||
bulmaswatch@^0.8.1:
|
||||
version "0.8.1"
|
||||
@@ -47,6 +47,11 @@ bulmaswatch@^0.8.1:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
css.gg@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css.gg/-/css.gg-2.0.0.tgz#1ba891092ceac58a2550540171c2d469a86b27b2"
|
||||
integrity sha512-8qO59kSXLbewo7xaUM4eC8328MTdGmb8atpmw6PRzeDGsZrCkAVjJzKeFDIntagl1/aCee+NG1OQtIvWAjgG/A==
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
@@ -67,9 +72,9 @@ glob-parent@~5.1.2:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
|
||||
integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
|
||||
integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
@@ -81,7 +86,7 @@ is-binary-path@~2.1.0:
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
|
||||
|
||||
is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
version "4.0.3"
|
||||
@@ -113,9 +118,9 @@ readdirp@~3.6.0:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
sass@^1.46.0:
|
||||
version "1.49.7"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.49.7.tgz#22a86a50552b9b11f71404dfad1b9ff44c6b0c49"
|
||||
integrity sha512-13dml55EMIR2rS4d/RDHHP0sXMY3+30e1TKsyXaSz3iLWVoDWEoboY8WzJd5JMnxrRHffKO3wq2mpJ0jxRJiEQ==
|
||||
version "1.55.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.55.0.tgz#0c4d3c293cfe8f8a2e8d3b666e1cf1bff8065d1c"
|
||||
integrity sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
@@ -0,0 +1,15 @@
|
||||
location / {
|
||||
# try to serve file directly, fallback to rewrite
|
||||
try_files $uri @rewriteapp;
|
||||
}
|
||||
|
||||
location @rewriteapp {
|
||||
# rewrite all to index.php
|
||||
rewrite ^(.*)$ /index.php/$1 last;
|
||||
}
|
||||
|
||||
location ~ ^/index\.php(/|$) {
|
||||
try_files @heroku-fcgi @heroku-fcgi;
|
||||
# ensure that /index.php isn't accessible directly, but only through a rewrite
|
||||
internal;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
|
||||
This facilities making the bulma.min.css file, install all dependencies using:
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
And then use:
|
||||
```
|
||||
yarn bulma
|
||||
```
|
||||
|
||||
This compiles bulma.scss to a css file and helps lowering the size of the css that will be sent to the user.
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "proxitok-scss",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bulma": "sass --style=compressed bulma.scss ../bulma.min.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"bulma": "^0.9.3",
|
||||
"bulmaswatch": "^0.8.1",
|
||||
"sass": "^1.46.0"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../../scss/node_modules/css.gg/icons/scss/home.scss","../../scss/node_modules/css.gg/icons/scss/options.scss","../../scss/node_modules/css.gg/icons/scss/info.scss","../../scss/node_modules/css.gg/icons/scss/code-slash.scss","../../scss/node_modules/css.gg/icons/scss/search.scss","../../scss/node_modules/css.gg/icons/scss/feed.scss","../../scss/node_modules/css.gg/icons/scss/eye.scss","../../scss/node_modules/css.gg/icons/scss/heart.scss","../../scss/node_modules/css.gg/icons/scss/comment.scss","../../scss/node_modules/css.gg/icons/scss/share.scss","../../scss/node_modules/css.gg/icons/scss/software-download.scss","../../scss/node_modules/css.gg/icons/scss/lock.scss","../../scss/node_modules/css.gg/icons/scss/lock-unlock.scss","../../scss/node_modules/css.gg/icons/scss/music.scss"],"names":[],"mappings":"AAAA,SACE,yLACA,sBACA,kBACA,cACA,+BACA,WACA,YACA,iBACA,aACA,gBACA,4BACA,2BACA,6BACA,4BACA,mBAEA,gBACE,WACA,cACA,sBACA,kBAGF,iBACE,WACA,cACA,sBACA,kBACA,qBACA,sBACA,2BACA,wBACA,SACA,kBACA,WACA,YACA,OAGF,gBACE,UACA,YACA,iBACA,oBACA,4BACA,6BACA,gBACA,SACA,SCjDJ,YACI,sBACA,kBACA,cACA,+BACA,WACA,WACA,qCAEA,mBACE,WACA,cACA,sBACA,kBACA,UACA,WACA,iBACA,mBAGF,oBACE,WACA,cACA,sBACA,kBACA,UACA,WACA,iBACA,mBACA,SACA,UAGF,mBACE,YACA,WCnCN,SACI,sBACA,kBACA,cACA,+BACA,WACA,YACA,iBACA,mBAEA,iCACE,WACA,cACA,sBACA,kBACA,kBACA,UACA,wBACA,SAGF,gBACE,WACA,WAGF,iBACE,WACA,QC5BN,eACI,cACA,kBACA,sBACA,6CACA,UACA,YACA,wBAEA,sBACE,WACA,cACA,sBACA,kBACA,UACA,WACA,yBAGF,uBACE,WACA,cACA,sBACA,kBACA,UACA,WACA,yBACA,sBACA,qBACA,UACA,QAGF,sBACE,uBACA,wBACA,WACA,QCrCN,WACI,sBACA,kBACA,cACA,+BACA,WACA,YACA,iBACA,mBACA,iBACA,gBAEA,kBACE,WACA,cACA,sBACA,kBACA,kBACA,UACA,WACA,wBACA,yBACA,SACA,UCvBN,SACI,cACA,sBACA,wBACA,sBASA,iBACA,gBACA,kBACA,+BACA,UACA,WACA,kBAbA,iCACE,cACA,sBACA,wBACA,sBAWF,iCACE,WACA,kBACA,kBACA,WACA,WACA,QACA,SACA,WAGF,gBACE,UACA,QACA,WCnCN,QACI,kBACA,cACA,+BACA,WACA,YACA,iCACA,gCACA,gBACA,sBAEA,+BACE,WACA,cACA,oBACA,kBACA,sBAGF,eACE,QACA,8CACA,WACA,YAGF,gBACE,UACA,WACA,iBACA,WACA,SC/BN,UACI,iBACA,6BACA,8BACA,WACA,WACA,gBAeA,sBACA,kBACA,gHACA,cAhBA,iBACE,iBACA,6BACA,8BACA,WACA,WACA,gBACA,WACA,cACA,sBACA,kBAQF,kBACE,WACA,cACA,sBACA,kBAGF,iBACE,WACA,wBACA,QAGF,kBACE,WACA,YACA,sBACA,wBACA,UACA,QC7CN,YACI,sBACA,kBACA,cACA,+BACA,WACA,YACA,iBACA,gBACA,0CAEA,mBACE,WACA,cACA,sBACA,kBACA,UAGF,oBACE,WACA,cACA,sBACA,kBACA,UACA,iBACA,+BACA,+BACA,UACA,YACA,WAGF,mBACE,WACA,wBACA,qBACA,SACA,QCtCN,UACI,sBACA,kBACA,cACA,+BACA,UACA,WACA,wBACA,oBACA,kCAEA,iBACE,WACA,cACA,sBACA,kBACA,kBACA,WACA,WACA,wBACA,SAGF,kBACE,WACA,cACA,sBACA,kBACA,kBACA,WACA,WACA,wBACA,SACA,MACA,yBAGF,iBACE,SACA,wBCvCN,sBACI,sBACA,kBACA,cACA,+BACA,WACA,WACA,iBACA,aACA,8BACA,+BACA,eAEA,6BACE,WACA,cACA,sBACA,kBACA,UACA,WACA,sBACA,wBACA,yBACA,SACA,WAGF,8BACE,WACA,cACA,sBACA,kBACA,kBACA,UACA,YACA,wBACA,SACA,WCrCN,SACI,sBACA,kBACA,cACA,+BACA,WACA,YACA,iBACA,4BACA,2BACA,4BACA,iBAEA,gBACE,WACA,cACA,sBACA,kBACA,WACA,YACA,kBACA,+BACA,qBACA,UACA,QCxBN,gBACI,sBACA,kBACA,cACA,+BACA,WACA,WACA,iBACA,8BACA,6BACA,4BACA,iBACA,iBAEA,+CACE,WACA,cACA,sBACA,kBAGF,uBACE,+BACA,qBACA,WACA,YACA,kBACA,UACA,SAGF,wBACE,UACA,WACA,wBACA,UACA,QCpCN,UACI,cACA,sBACA,iBAQA,2BACA,4BACA,gBACA,gBACA,kBACA,6CACA,WACA,YAbA,mCACE,cACA,sBACA,iBAYF,mCACE,WACA,kBACA,UACA,WACA,QACA,UACA,uBACA,mBAGF,iBACE","file":"cssgg.min.css"}
|
||||
@@ -0,0 +1 @@
|
||||
{include './themes/' . theme() . '.latte'}
|
||||
@@ -0,0 +1,10 @@
|
||||
<form action="{path($path)}" method="{$method}">
|
||||
{block fields}{/block}
|
||||
{ifset $submit}
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<button class="button is-success" type="submit">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
{/ifset}
|
||||
</form>
|
||||
@@ -0,0 +1,19 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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')}">
|
||||
<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" />
|
||||
{if isset($has_rss)}
|
||||
<link rel="alternate" type="application/rss+xml" title="{$title}" href="{$_SERVER['REQUEST_URI'] . '/rss'}" />
|
||||
{/if}
|
||||
<link rel="stylesheet" href="{static('css', 'cssgg.min.css', true)}">
|
||||
<link rel="stylesheet" href="{static('css', 'bulma.min.css', true)}">
|
||||
<title>{$title} - ProxiTok</title>
|
||||
</head>
|
||||
@@ -0,0 +1,14 @@
|
||||
{define icon_common, $icon}
|
||||
<span class="icon">
|
||||
<i class="gg-{$icon}"></i>
|
||||
</span>
|
||||
{/define}
|
||||
|
||||
{if isset($text)}
|
||||
<span class="icon-text">
|
||||
{include icon_common, icon: $icon}
|
||||
<span>{$text}</span>
|
||||
</span>
|
||||
{else}
|
||||
{include icon_common, icon: $icon}
|
||||
{/if}
|
||||
@@ -0,0 +1,29 @@
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a href="{path('/')}" class="navbar-item">
|
||||
{include './icon.latte', icon: 'home', text: 'ProxiTok'}
|
||||
</a>
|
||||
<a role="button" id="navbar-burger" class="navbar-burger" aria-label="menu" aria-expanded="false">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbar-menu" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a href="{path('/settings')}" class="navbar-item">
|
||||
{include './icon.latte', icon: 'options', text: 'Settings'}
|
||||
</a>
|
||||
<a href="{path('/about')}" class="navbar-item">
|
||||
{include './icon.latte', icon: 'info', text: 'About'}
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<a href="https://github.com/pablouser1/ProxiTok" class="navbar-item" target="_blank">
|
||||
{include './icon.latte', icon: 'code-slash', text: 'Source'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<script src="{static('js', 'navbar.js')}"></script>
|
||||
@@ -0,0 +1,3 @@
|
||||
<a href="{$_SERVER['REQUEST_URI'] . '/rss'}">
|
||||
{include './icon.latte', icon: 'feed', text: 'RSS Feed'}
|
||||
</a>
|
||||
@@ -0,0 +1,31 @@
|
||||
{embed '../form.latte', path: '/settings/api', method: 'POST', submit: true}
|
||||
{block fields}
|
||||
<div class="field">
|
||||
<label class="label">Use test endpoints</label>
|
||||
<div class="control">
|
||||
<label class="radio">
|
||||
<input type="radio" name="api-test_endpoints" value="yes" n:attr="checked => $isTestEndpoints" />
|
||||
<span>Yes</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="api-test_endpoints" value="no" n:attr="checked => !$isTestEndpoints" />
|
||||
<span>No</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">This <i>may</i> help bypass rate limits</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Downloader</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="api-downloader">
|
||||
{foreach $downloaders as $downloader}
|
||||
<option value="{$downloader}" n:attr="selected => $currentDownloader === $downloader">{$downloader|firstUpper}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help">Watermark downloads will <i>not</i> use third party methods</p>
|
||||
</div>
|
||||
{/block}
|
||||
{/embed}
|
||||
@@ -0,0 +1,16 @@
|
||||
{embed '../form.latte', path: '/settings/general', method: 'POST', submit: true}
|
||||
{block fields}
|
||||
<div class="field">
|
||||
<label class="label">Theme</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="theme">
|
||||
{foreach $themes as $theme}
|
||||
<option value="{$theme}" n:attr="selected => $currentTheme === $theme">{$theme|firstUpper}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
{/embed}
|
||||
@@ -0,0 +1,70 @@
|
||||
<link rel="stylesheet" href="{static('css', 'themes/card.css')}">
|
||||
<noscript>JavaScript is required for this section to work!</noscript>
|
||||
<section class="section">
|
||||
<div class="columns is-multiline is-vcentered">
|
||||
{foreach $data->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)}"
|
||||
data-video_download_watermark="{url_download($item->video->playAddr, $item->author->uniqueId, $item->id, true)}"
|
||||
data-video_download_nowatermark="{url_download(url_video_external($item->author->uniqueId, $item->id), $item->author->uniqueId, $item->id, false)}"
|
||||
data-video_share_url="{$share_url}"
|
||||
data-desc="{$item->desc}"
|
||||
data-createtime="{$item->createTime}"
|
||||
data-music_title="{$item->music->title}"
|
||||
data-music_url="{url_stream($item->music->playUrl)}">
|
||||
<img loading="lazy" src="{url_stream($item->video->originCover)}" />
|
||||
<img class="hidden" loading="lazy" data-src="{url_stream($item->video->dynamicCover)}" />
|
||||
</div>
|
||||
{/foreach}
|
||||
{if empty($data->feed->items)}
|
||||
<p class="title">No items sent by TikTok!</p>
|
||||
{/if}
|
||||
</div>
|
||||
{include './common/controls.latte'}
|
||||
</section>
|
||||
|
||||
<!-- MODAL -->
|
||||
<div id="modal" class="modal">
|
||||
<div id="modal-background" class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<button id="modal-close" class="delete" aria-label="close"></button>
|
||||
<p class="modal-card-title" id="item_title"></p>
|
||||
</header>
|
||||
<section class="modal-card-body has-text-centered" style="overflow: hidden;">
|
||||
<video id="video" autoplay controls></video>
|
||||
</section>
|
||||
<footer class="modal-card-foot has-text-centered">
|
||||
<div class="container">
|
||||
<p id="item_date"></p>
|
||||
<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 id="download_dropdown" class="dropdown is-hoverable">
|
||||
<div class="dropdown-trigger">
|
||||
<button id="download_button" class="button" aria-haspopup="true" aria-controls="dropdown-menu">Download</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a id="download_watermark" target="_blank" class="dropdown-item">With watermark</a>
|
||||
<a id="download_nowatermark" target="_blank" class="dropdown-item">Without watermark</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p id="audio_title"></p>
|
||||
<audio id="audio" controls preload="none"></audio>
|
||||
<div class="buttons is-centered">
|
||||
<button id="back-button" class="button is-danger">Back</button>
|
||||
<button id="next-button" class="button is-success">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{static('js', 'themes/card.js')}"></script>
|
||||
@@ -0,0 +1,7 @@
|
||||
<div n:ifset="$data->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>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
<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">
|
||||
{include '../../icon.latte', icon: 'lock', text: 'Instance link'}
|
||||
</a>
|
||||
<a href="{url_video_external($uniqueId, $id)}" class="dropdown-item has-text-warning">
|
||||
{include '../../icon.latte', icon: 'lock-unlock', text: 'Original Link'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<p>
|
||||
{include '../../icon.latte', icon: 'eye', text: number($playCount)}
|
||||
{include '../../icon.latte', icon: 'heart', text: number($diggCount)}
|
||||
{include '../../icon.latte', icon: 'comment', text: number($commentCount)}
|
||||
{include '../../icon.latte', icon: 'share', text: number($shareCount)}
|
||||
</p>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="tags">
|
||||
{foreach $challenges as $challenge}
|
||||
<a class="tag is-rounded" href="{path('/tag/' . $challenge->title)}">#{$challenge->title}</a>
|
||||
{/foreach}
|
||||
</div>
|
||||
@@ -0,0 +1,38 @@
|
||||
<div class="container">
|
||||
{foreach $data->feed->items as $item}
|
||||
<article class="media">
|
||||
<figure class="media-left">
|
||||
<p class="image is-64x64">
|
||||
<img src="{url_stream($item->author->avatarThumb)}" />
|
||||
</p>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<p>
|
||||
<strong>{$item->author->nickname}</strong>
|
||||
<small>
|
||||
<a href="{url_user($item->author->uniqueId)}">@{$item->author->uniqueId}</a>
|
||||
</small>
|
||||
<small title="{date('M d, Y H:i:s e', $item->createTime)}">{date('M d, Y', $item->createTime)}</small>
|
||||
</p>
|
||||
{if !empty($item->challenges)}
|
||||
<p>{include './common/tags.latte', challenges: $item->challenges}</p>
|
||||
{/if}
|
||||
<p n:ifcontent>{$item->desc}</p>
|
||||
{include './common/stats.latte', playCount: $item->stats->playCount, diggCount: $item->stats->diggCount, commentCount: $item->stats->commentCount, shareCount: $item->stats->shareCount}
|
||||
<div class="has-text-centered">
|
||||
<video width="{$item->video->width}" height="{$item->video->height}" controls preload="none" poster="{url_stream($item->video->originCover)}">
|
||||
<source src="{url_stream($item->video->playAddr)}" type="video/mp4" />
|
||||
</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>
|
||||
</div>
|
||||
</article>
|
||||
{/foreach}
|
||||
{if empty($data->feed->items)}
|
||||
<p class="title">No items sent by TikTok!</p>
|
||||
{/if}
|
||||
</div>
|
||||
{include './common/controls.latte'}
|
||||
@@ -15,7 +15,6 @@
|
||||
<section class="section">
|
||||
{block content}{/block}
|
||||
</section>
|
||||
{include '../components/footer.latte'}
|
||||
{block extra}{/block}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
{include '../components/head.latte'}
|
||||
|
||||
<body>
|
||||
{block content}{/block}
|
||||
{block extra}{/block}
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,9 +12,6 @@
|
||||
<div class="container has-text-centered">
|
||||
{block content}{/block}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-foot">
|
||||
{include '../components/footer.latte'}
|
||||
</div>
|
||||
</section>
|
||||
{block extra}{/block}
|
||||
@@ -0,0 +1,40 @@
|
||||
{layout '../layouts/default.latte'}
|
||||
|
||||
{block header}
|
||||
<p class="title">Welcome to ProxiTok!</p>
|
||||
<p class="subtitle">
|
||||
Made with <span style="color: #e25555;">♥</span> in <a href="https://github.com/pablouser1/ProxiTok">Github</a>
|
||||
</p>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="content">
|
||||
<p class="title">About this instance</p>
|
||||
<p>Frontend version: <b>{version_frontend()}</b></p>
|
||||
<p>Scraper version: <b>{version_scraper()}</b></p>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="title">Why would I want to use ProxiTok?</p>
|
||||
<p>
|
||||
There are already
|
||||
<a rel="nofollow" href="https://penetrum.com/tiktok/Penetrum_TikTok_Security_Analysis_whitepaper.pdf">multiple</a>
|
||||
<a rel="nofollow" href="https://rufposten.de/blog/2019/12/05/privacy-analysis-of-tiktoks-app-and-website">articles</a>
|
||||
<a rel="nofollow" href="https://citizenlab.ca/2021/03/tiktok-vs-douyin-security-privacy-analysis">explaining</a>
|
||||
Tiktok's privacy issues.
|
||||
This program allows you to browse TikTok without having to worry about privacy concerns.
|
||||
</p>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="title">Third party</p>
|
||||
<p>
|
||||
This project wouldn't be possible without the help of the following projects:
|
||||
<ul>
|
||||
<li><a rel="nofollow" href="https://github.com/pablouser1/TikScraperPHP">TikScraperPHP</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/josegonzalez/php-dotenv">php-dotenv</a></li>
|
||||
<li><a rel="nofollow" href="https://github.com/jgthms/bulma">Bulma</a> and <a href="https://github.com/jenil/bulmaswatch">Bulmaswatch</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
{/block}
|
||||
@@ -5,7 +5,7 @@
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
{foreach $feed->items as $type => $items}
|
||||
{foreach $data->feed->items as $type => $items}
|
||||
<p class="title">{$type|firstUpper}</p>
|
||||
<div class="columns is-multiline is-vcentered">
|
||||
{foreach $items as $item}
|
||||
@@ -17,7 +17,7 @@
|
||||
{if !empty($item->cardItem->cover)}
|
||||
<div class="media-left">
|
||||
<figure class="image is-96x96">
|
||||
<img width="96" height="96" src="{path('/stream?url=' . urlencode($item->cardItem->cover))}" />
|
||||
<img loading="lazy" width="96" height="96" src="{path('/stream?url=' . urlencode($item->cardItem->cover))}" />
|
||||
</figure>
|
||||
</div>
|
||||
{/if}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user