Compare commits

...

6 Commits

Author SHA1 Message Date
Pablo Ferreiro fccec753d9 No watermark without external service 2022-02-07 23:45:07 +01:00
Pablo Ferreiro 7203bc1509 Share url, removed hash 2022-02-07 22:23:33 +01:00
Pablo Ferreiro d871ed4e68 hotfix 2022-02-07 21:13:34 +01:00
Pablo Ferreiro 2c73bd0388 Allow download without watermark 2022-02-07 21:07:51 +01:00
Pablo Ferreiro dd5964cb97 Fixed /video and set user on video download 2022-02-07 18:40:32 +01:00
Pablo Ferreiro 19553f8837 Workaround for back btn. Added id to vid download 2022-02-07 18:32:45 +01:00
8 changed files with 126 additions and 93 deletions
+1 -1
View File
@@ -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
+38 -9
View File
@@ -9,25 +9,54 @@ 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');
}
}
static private function getFileName(): string {
$filename = 'tiktok-video';
if (isset($_GET['user'])) {
$filename .= '-' . $_GET['user'] . '-' . $_GET['id'];
}
return $filename;
}
static public function stream() {
if (isset($_GET['download'])) {
// Download
$downloader = new \Sovit\TikTok\Download();
$downloader->url($url, "tiktok-video", 'mp4');
$watermark = isset($_GET['watermark']);
if ($watermark) {
self::checkUrl();
$filename = self::getFileName();
$downloader = new \Sovit\TikTok\Download();
$downloader->url($_GET['url'], $filename, true);
} else {
if (!isset($_GET['id'])) {
die('You need to send an ID!');
}
$filename = self::getFileName();
$downloader = new \Sovit\TikTok\Download();
$downloader->url($_GET['id'], $filename, false);
}
} else {
// Stream
self::checkUrl();
$url = $_GET['url'];
$streamer = new \Sovit\TikTok\Stream();
$streamer->stream($url);
}
+28 -16
View File
@@ -3,25 +3,26 @@
<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>
{do $share_url = 'https://tiktok.com/@' . $item->author->uniqueId . '/' . $item->id}
<div class="column is-one-quarter clickable-img" id="{$item->id}" onclick="openVideo(this.id)"
data-video_url="{path('/stream?url=' . urlencode($item->video->playAddr))}"
data-video_download_watermark="{path('/stream?url=' . urlencode($item->video->playAddr) . '&download=1&id=' . $item->id . '&user=' . $item->author->uniqueId) . '&watermark='}"
data-video_download_nowatermark="{path('/stream?download=1&id=' . $item->id . '&user=' . $item->author->uniqueId)}"
data-video_share_url="{$share_url}"
data-desc="{$item->desc}"
data-music_title="{$item->music->title}"
data-music_url="{path('/stream?url=' . urlencode($item->music->playUrl))}">
<img loading="lazy" src="{path('/stream?url=' . urlencode($item->video->originCover))}" />
<img class="hidden" loading="lazy" data-src="{path('/stream?url=' . urlencode($item->video->dynamicCover))}" />
</div>
{/foreach}
</div>
<div n:ifset="$feed->info" class="buttons">
{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 isset($_GET['cursor']) && $_GET['cursor'] != 0 }
<a class="button is-danger" href="?">First</a>
<button class="button is-danger" onclick="history.back()">Back</button>
{/if}
<a n:attr="disabled => $feed->hasMore" class="button is-success" href="?cursor={$feed->maxCursor}">Next</a>
<a n:attr="disabled => !$feed->hasMore" class="button is-success" href="?cursor={$feed->maxCursor}">Next</a>
</div>
</section>
@@ -34,11 +35,22 @@
<p class="modal-card-title" id="item_title"></p>
</header>
<section class="modal-card-body has-text-centered" style="overflow: hidden;">
<video id="video" controls preload="none"></video>
<video id="video" autoplay controls></video>
</section>
<footer class="modal-card-foot has-text-centered">
<div class="container">
<a id="download_button" target="_blank" class="button is-info" download>Download</a>
<div class="field has-addons has-addons-centered">
<div class="control">
<input id="share_input" class="input" readonly />
</div>
<div class="control">
<button class="button is-primary" onclick="copyShare()">Copy</button>
</div>
</div>
<div class="buttons is-centered">
<a target="_blank" id="download_watermark" class="button is-info">Download with watermark</a>
<a target="_blank" id="download_nowatermark" class="button is-info">Download without watermark</a>
</div>
<p id="audio_title"></p>
<audio id="audio" controls preload="none"></audio>
<div class="buttons is-centered">
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "pablouser1/proxitok",
"description": "An alternative frontend for TikTok",
"version": "1.4.1",
"version": "1.4.1.4",
"license": "AGPL-3.0-or-later",
"type": "project",
"homepage": "https://github.com/pablouser1/ProxiTok",
Generated
+5 -5
View File
@@ -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": "e829a003ddc8a31048406dfd3ec20815",
"packages": [
{
"name": "bramus/router",
@@ -381,12 +381,12 @@
"source": {
"type": "git",
"url": "https://github.com/pablouser1/TikTok-API-PHP.git",
"reference": "c6667d70757fd5405ee1b51eaa6602d8758d2abd"
"reference": "60c00cfd321888b80eba93b6214c8c4003e98e57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pablouser1/TikTok-API-PHP/zipball/c6667d70757fd5405ee1b51eaa6602d8758d2abd",
"reference": "c6667d70757fd5405ee1b51eaa6602d8758d2abd",
"url": "https://api.github.com/repos/pablouser1/TikTok-API-PHP/zipball/60c00cfd321888b80eba93b6214c8c4003e98e57",
"reference": "60c00cfd321888b80eba93b6214c8c4003e98e57",
"shasum": ""
},
"type": "library",
@@ -425,7 +425,7 @@
"issues": "https://github.com/ssovit/TikTok-API-PHP/issues",
"email": "sovit.tamrakar@gmail.com"
},
"time": "2022-02-06T13:17:16+00:00"
"time": "2022-02-07T22:43:53+00:00"
},
{
"name": "symfony/polyfill-ctype",
+43 -55
View File
@@ -1,16 +1,18 @@
var opened_video_id = null
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')
const download_watermark = document.getElementById('download_watermark')
const download_nowatermark = document.getElementById('download_nowatermark')
const share_input = document.getElementById('share_input')
// -- HELPERS -- //
const getHash = () => window.location.hash.substring(1)
const getVideoDataById = (id) => {
const getVideoDataById = id => {
const el = document.getElementById(id)
if (el) {
opened_video_id = id
return el.dataset
}
return false
@@ -18,18 +20,20 @@ const getVideoDataById = (id) => {
const isModalActive = () => modal.classList.contains('is-active')
const toggleButton = (id, force) => document.getElementById(id) ? document.getElementById(id).toggleAttribute('disabled', force) : alert('That button does not exist')
const toggleButton = (id, force) => document.getElementById(id).toggleAttribute('disabled', force)
// -- MODAL -- //
const swapData = ({ video_url, desc, video_download, music_title, music_url }) => {
const swapData = ({ video_url, desc, video_download_watermark, video_download_nowatermark, video_share_url, music_title, music_url }) => {
video.src = video_url
item_title.innerText = desc
download_button.href = video_download
download_watermark.href = video_download_watermark
download_nowatermark.href = video_download_nowatermark
share_input.value = video_share_url
audio_title.innerText = music_title
audio.src = music_url
}
const showModal = (id) => {
const showModal = id => {
const dataset = getVideoDataById(id)
if (dataset) {
swapData(dataset)
@@ -48,27 +52,27 @@ const hideModel = () => {
history.pushState('', document.title, window.location.pathname + window.location.search)
}
const getPrevOrNext = (forward) => {
const hash = getHash()
if (hash) {
const el = document.getElementById(hash)
const getPrevOrNext = forward => {
if (opened_video_id) {
const el = document.getElementById(opened_video_id)
if (el) {
if (forward) {
return el.parentElement.nextElementSibling ? el.parentElement.nextElementSibling.children[0] : null
return el.nextElementSibling
}
return el.parentElement.previousElementSibling ? el.parentElement.previousElementSibling.children[0] : null
return el.previousElementSibling
}
}
return null
}
const moveVideo = (forward) => {
const moveVideo = forward => {
// Re-enable buttons
toggleButton('back-button', false)
toggleButton('next-button', false)
const new_el = getPrevOrNext(forward)
if (new_el) {
window.location.hash = new_el.id
opened_video_id = new_el.id
swapData(new_el.dataset)
} else {
// Max reached, disable buttons depending on direction
if (forward) {
@@ -80,58 +84,42 @@ const moveVideo = (forward) => {
}
// EVENTS //
const hashChange = () => {
if (window.location.hash) {
const hash = getHash()
if (hash) {
// Check first if the modal is already active
if (isModalActive()) {
// If it is, get hash video id and swap data
const dataset = getVideoDataById(hash)
if (dataset) {
swapData(dataset)
video.play()
}
} else {
showModal(hash)
}
const openVideo = video_id => {
if (isModalActive()) {
const dataset = getVideoDataById(video_id)
if (dataset) {
swapData(dataset)
}
} else {
showModal(video_id)
}
}
const swapImages = (e, mode) => {
let img = null
let gif = null
if (mode === 'gif') {
const a = e.target
img = a.children[0]
gif = a.children[1]
} else if (mode === 'img') {
gif = e.target
img = e.target.parentElement.children[0]
const swapImages = e => {
const div = e.target
const img = div.children[0]
const gif = div.children[1]
if (!gif.src) {
gif.src = gif.dataset.src
}
img.classList.toggle('hidden')
gif.classList.toggle('hidden')
}
if (img && gif) {
if (!gif.src) {
gif.src = gif.dataset.src
}
img.classList.toggle('hidden')
gif.classList.toggle('hidden')
}
const copyShare = () => {
share_input.select();
navigator.clipboard.writeText(share_input.value);
alert('Copied!')
}
document.getElementById('modal-background').addEventListener('click', hideModel)
document.getElementById('modal-close').addEventListener('click', hideModel)
document.getElementById('back-button').addEventListener('click', () => moveVideo(false))
document.getElementById('next-button').addEventListener('click', () => moveVideo(true))
window.addEventListener('hashchange', hashChange, false)
// Image hover
const images = document.getElementsByClassName("clickable-img")
for (let i = 0; i < images.length; i++) {
images[i].addEventListener('mouseenter', e => swapImages(e, 'gif'), false)
images[i].addEventListener('mouseout', e => swapImages(e, 'img'), false)
images[i].addEventListener('mouseenter', swapImages, false)
images[i].addEventListener('mouseout', swapImages, false)
}
hashChange()
+5 -1
View File
@@ -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;
}
+5 -5
View File
@@ -3,20 +3,20 @@
{block content}
<div class="columns is-centered is-vcentered">
<div class="column">
<video controls poster="{path('stream?url=' . urlencode($item->items[0]->video->originCover))}">
<source src="{path('stream?url=' . urlencode($item->items[0]->video->playAddr))}" type="video/mp4" />
<video controls poster="{path('/stream?url=' . urlencode($item->items[0]->video->originCover))}">
<source src="{path('/stream?url=' . urlencode($item->items[0]->video->playAddr))}" type="video/mp4" />
</video>
</div>
<div class="column has-text-centered">
<div class="box">
<p class="title">Video by <a href="{path('@'.$item->info->detail->user->uniqueId)}">{$item->info->detail->user->uniqueId}</a></p>
<p class="title">Video by <a href="{path('/@'.$item->info->detail->user->uniqueId)}">{$item->info->detail->user->uniqueId}</a></p>
<p class="subtitle">{$item->items[0]->desc}</p>
<p>Played {number($item->info->detail->stats->playCount)} times</p>
<p>Shared {number($item->info->detail->stats->shareCount)} times / {number($item->info->detail->stats->commentCount)} comments</p>
<hr />
<a href="{path('stream?url=' . urlencode($item->items[0]->video->playAddr) . '&download=1')}" class="button is-info">Download video</a>
<a href="{path('/stream?url=' . urlencode($item->items[0]->video->playAddr) . '&download=1')}" class="button is-info">Download video</a>
<p>{$item->items[0]->music->title}</p>
<audio src="{path('stream?url=' . urlencode($item->items[0]->music->playUrl))}" controls preload="none"></audio>
<audio src="{path('/stream?url=' . urlencode($item->items[0]->music->playUrl))}" controls preload="none"></audio>
</div>
</div>
</div>