Hash system and controls inside player

This commit is contained in:
Pablo Ferreiro 2022-01-02 00:06:00 +01:00
parent c8d991883a
commit e9b194217a
No known key found for this signature in database
GPG key ID: 41FBCE65B779FA24
8 changed files with 151 additions and 71 deletions

0
.env
View file

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/.env
/.vscode
/vendor
/cache/views/*

View file

@ -1,4 +1,5 @@
# TikTok alternative Frontend
Use Tiktok using an alternative frontend, inspired by Nitter.
## Installation
Clone the repository and fetch the requiered external packages with:
@ -14,6 +15,9 @@ php -S localhost:8080
## Known issues
* Right now there is an error when trying to fetch the desired user, there is already a pull request not merged yet fixing this issue on the TikTokApi repo, you can check it out [here](https://github.com/ssovit/TikTok-API-PHP/pull/43)
## TODO
* Allow searching for just one video using the ID
## Credits
* [TikTok-API-PHP](https://github.com/ssovit/TikTok-API-PHP)
* [Leaf Framework](https://github.com/leafsphp/leaf)

View file

@ -9,7 +9,17 @@ $app->get('/', function () use ($app) {
$app->response()->page('./views/home.html');
});
$app->get("/users/{user}", function (string $username) {
$app->get('/stream', function () {
if (!isset($_GET['url'])) {
die('You need to send a url!');
}
// Start streamer
$streamer = new \Sovit\TikTok\Stream();
$streamer->stream($_GET['url']);
});
$app->get("/@([^/]+)", function (string $username) {
$cursor = 0;
if (isset($_GET['cursor']) && is_numeric($_GET['cursor'])) {
$cursor = (int) $_GET['cursor'];
@ -24,14 +34,4 @@ $app->get("/users/{user}", function (string $username) {
}
});
$app->get('/stream', function () {
if (!isset($_GET['url'])) {
die('You need to send a url!');
}
// Start streamer
$streamer = new \Sovit\TikTok\Stream();
$streamer->stream($_GET['url']);
});
$app->run();

View file

@ -3,38 +3,108 @@ 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 = document.getElementById('download_button')
const download_button = document.getElementById('download_button')
const showModal = (e) => {
video.src = e.target.dataset.video_url
video.width = e.target.dataset.video_width
video.height = e.target.dataset.video_height
item_title.innerText = e.target.dataset.desc
download.href = e.target.dataset.video_download
audio_title.innerText = e.target.dataset.music_title
audio.src = e.target.dataset.music_url
// -- 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, video_width, video_height, desc, video_download, music_title, music_url }) => {
video.src = video_url
video.width = video_width
video.height = video_height
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')
video.width = ''
video.height = ''
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 //
// Click to show modal
const figures = document.getElementsByClassName("clickable-img")
for (let i = 0; i < figures.length; i++) {
const figure = figures[i]
figure.addEventListener('click', showModal, true)
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)
}
}
}
}
// Click to hide modal
document.getElementById('modal-background').addEventListener('click', hideModel, true)
document.getElementById('modal-close').addEventListener('click', hideModel, true)
document.getElementById('modal-background').addEventListener('click', hideModel, false)
document.getElementById('modal-close').addEventListener('click', hideModel, false)
document.getElementById('back-button').addEventListener('click', () => moveVideo(false))
document.getElementById('next-button').addEventListener('click', () => moveVideo(true))
window.addEventListener('hashchange', hashChange, false)
hashChange()

View file

@ -1,9 +1,3 @@
/*
.modal-card-body {
background-color: transparent;
}
*/
#video {
height: 100%;
overflow: hidden;

View file

@ -15,21 +15,26 @@
<div class="container has-text-centered">
<p class="title">Welcome to TikTok Viewer!</p>
<p class="subtitle">Alternative TikTok Frontend</p>
<div class="field has-addons has-addons-centered">
<div class="control">
<input id="username_input" class="input" placeholder="Type username" required />
<form id="username_form">
<div class="field has-addons has-addons-centered">
<div class="control">
<input name="username" class="input" placeholder="Type username" required />
</div>
<div class="control">
<button class="button is-success" type="submit">Go</button>
</div>
</div>
<div class="control">
<button id="username_send" class="button is-success" type="button">Go</button>
</div>
</div>
</form>
</div>
</section>
<script>
document.getElementById('username_send').addEventListener('click', () => {
const username = document.getElementById('username_input').value
window.location.href = `./users/${username}`
})
const goToUser = (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const username = formData.get('username')
window.location.href = `./@${username}`
}
document.getElementById('username_form').addEventListener('submit', goToUser, false)
</script>
</body>

View file

@ -21,25 +21,27 @@
<div class="columns is-multiline is-vcentered">
@foreach ($user->items as $item)
<div class="column is-one-quarter">
<figure class="image clickable-img">
<img data-video_url="{{ $item->video->playAddr }}"
data-video_download="{{ $item->video->downloadAddr }}"
data-desc="{{ $item->desc }}"
data-video_width="{{ $item->video->width }}"
data-video_height="{{ $item->video->height }}"
data-music_title="{{ $item->music->title }}"
data-music_url="{{ $item->music->playUrl }}"
src="{{ $item->video->originCover }}"/>
</figure>
<a id="{{$item->id}}" href="#{{$item->id}}" class="clickable-img" data-video_url="{{ $item->video->playAddr }}"
data-video_download="{{ $item->video->downloadAddr }}"
data-desc="{{ $item->desc }}"
data-video_width="{{ $item->video->width }}"
data-video_height="{{ $item->video->height }}"
data-music_title="{{ $item->music->title }}"
data-music_url="{{ $item->music->playUrl }}">
<img src="{{ $item->video->originCover }}"/>
</a>
</div>
@endforeach
</div>
<div class="buttons">
<a class="button" href="javascript:history.back()">Last page</a>
@isset ($_GET['cursor'])
<a class="button is-danger" href="?cursor=0">First</a>
@endisset
<a class="button is-danger" href="javascript:history.back()">Back</a>
@if ($user->hasMore)
<a class="button" href="?cursor={{ $user->maxCursor }}">Next page</a>
<a class="button is-success" href="?cursor={{ $user->maxCursor }}">Next</a>
@else
<a class="button is-disabled">Next page</a>
<a class="button is-success" disabled title="No more videos available">Next</a>
@endif
</div>
</section>
@ -47,17 +49,21 @@
<div id="modal-background" class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title" id="item_title"></p>
<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-footer has-text-centered">
<div class="box">
<a id="download_button" target="_blank" class="button is-info" href="#" download>Download</a>
<p id="audio_title"></p>
<audio id="audio" controls></audio>
<footer class="modal-card-foot has-text-centered">
<div class="container">
<a id="download_button" target="_blank" class="button is-info" href="" download>Download</a>
<p id="audio_title"></p>
<audio id="audio" controls></audio>
<div class="buttons">
<button id="back-button" class="button is-danger">Back</button>
<button id="next-button" class="button is-success">Next</button>
</div>
</div>
</footer>
</div>