Hash system and controls inside player
This commit is contained in:
parent
c8d991883a
commit
e9b194217a
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
/.env
|
||||
/.vscode
|
||||
/vendor
|
||||
/cache/views/*
|
||||
|
|
|
@ -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)
|
||||
|
|
22
index.php
22
index.php
|
@ -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();
|
||||
|
|
118
scripts/user.js
118
scripts/user.js
|
@ -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()
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
/*
|
||||
.modal-card-body {
|
||||
background-color: transparent;
|
||||
}
|
||||
*/
|
||||
|
||||
#video {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue