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
|
/.vscode
|
||||||
/vendor
|
/vendor
|
||||||
/cache/views/*
|
/cache/views/*
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# TikTok alternative Frontend
|
# TikTok alternative Frontend
|
||||||
|
Use Tiktok using an alternative frontend, inspired by Nitter.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Clone the repository and fetch the requiered external packages with:
|
Clone the repository and fetch the requiered external packages with:
|
||||||
|
@ -14,6 +15,9 @@ php -S localhost:8080
|
||||||
## Known issues
|
## 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)
|
* 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
|
## Credits
|
||||||
* [TikTok-API-PHP](https://github.com/ssovit/TikTok-API-PHP)
|
* [TikTok-API-PHP](https://github.com/ssovit/TikTok-API-PHP)
|
||||||
* [Leaf Framework](https://github.com/leafsphp/leaf)
|
* [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->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;
|
$cursor = 0;
|
||||||
if (isset($_GET['cursor']) && is_numeric($_GET['cursor'])) {
|
if (isset($_GET['cursor']) && is_numeric($_GET['cursor'])) {
|
||||||
$cursor = (int) $_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();
|
$app->run();
|
||||||
|
|
110
scripts/user.js
110
scripts/user.js
|
@ -3,38 +3,108 @@ const item_title = document.getElementById('item_title')
|
||||||
const audio = document.getElementById('audio')
|
const audio = document.getElementById('audio')
|
||||||
const audio_title = document.getElementById('audio_title')
|
const audio_title = document.getElementById('audio_title')
|
||||||
const modal = document.getElementById('modal')
|
const modal = document.getElementById('modal')
|
||||||
const download = document.getElementById('download_button')
|
const download_button = document.getElementById('download_button')
|
||||||
|
|
||||||
const showModal = (e) => {
|
// -- HELPERS -- //
|
||||||
video.src = e.target.dataset.video_url
|
const getHash = () => window.location.hash.substring(1)
|
||||||
video.width = e.target.dataset.video_width
|
|
||||||
video.height = e.target.dataset.video_height
|
const getVideoDataById = (id) => {
|
||||||
item_title.innerText = e.target.dataset.desc
|
const el = document.getElementById(id)
|
||||||
download.href = e.target.dataset.video_download
|
if (el) {
|
||||||
audio_title.innerText = e.target.dataset.music_title
|
return el.dataset
|
||||||
audio.src = e.target.dataset.music_url
|
}
|
||||||
|
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')
|
modal.classList.toggle('is-active')
|
||||||
video.play()
|
video.play()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hideModel = () => {
|
const hideModel = () => {
|
||||||
video.pause()
|
video.pause()
|
||||||
audio.pause()
|
audio.pause()
|
||||||
video.currentTime = 0
|
video.currentTime = 0
|
||||||
modal.classList.toggle('is-active')
|
modal.classList.toggle('is-active')
|
||||||
video.width = ''
|
toggleButton('back-button', false)
|
||||||
video.height = ''
|
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 //
|
// EVENTS //
|
||||||
|
const hashChange = () => {
|
||||||
// Click to show modal
|
if (window.location.hash) {
|
||||||
const figures = document.getElementsByClassName("clickable-img")
|
const hash = getHash()
|
||||||
for (let i = 0; i < figures.length; i++) {
|
if (hash) {
|
||||||
const figure = figures[i]
|
// Check first if the modal is already active
|
||||||
figure.addEventListener('click', showModal, true)
|
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, false)
|
||||||
document.getElementById('modal-background').addEventListener('click', hideModel, true)
|
document.getElementById('modal-close').addEventListener('click', hideModel, false)
|
||||||
document.getElementById('modal-close').addEventListener('click', hideModel, true)
|
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 {
|
#video {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -15,21 +15,26 @@
|
||||||
<div class="container has-text-centered">
|
<div class="container has-text-centered">
|
||||||
<p class="title">Welcome to TikTok Viewer!</p>
|
<p class="title">Welcome to TikTok Viewer!</p>
|
||||||
<p class="subtitle">Alternative TikTok Frontend</p>
|
<p class="subtitle">Alternative TikTok Frontend</p>
|
||||||
|
<form id="username_form">
|
||||||
<div class="field has-addons has-addons-centered">
|
<div class="field has-addons has-addons-centered">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input id="username_input" class="input" placeholder="Type username" required />
|
<input name="username" class="input" placeholder="Type username" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button id="username_send" class="button is-success" type="button">Go</button>
|
<button class="button is-success" type="submit">Go</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('username_send').addEventListener('click', () => {
|
const goToUser = (e) => {
|
||||||
const username = document.getElementById('username_input').value
|
e.preventDefault()
|
||||||
window.location.href = `./users/${username}`
|
const formData = new FormData(e.target)
|
||||||
})
|
const username = formData.get('username')
|
||||||
|
window.location.href = `./@${username}`
|
||||||
|
}
|
||||||
|
document.getElementById('username_form').addEventListener('submit', goToUser, false)
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -21,25 +21,27 @@
|
||||||
<div class="columns is-multiline is-vcentered">
|
<div class="columns is-multiline is-vcentered">
|
||||||
@foreach ($user->items as $item)
|
@foreach ($user->items as $item)
|
||||||
<div class="column is-one-quarter">
|
<div class="column is-one-quarter">
|
||||||
<figure class="image clickable-img">
|
<a id="{{$item->id}}" href="#{{$item->id}}" class="clickable-img" data-video_url="{{ $item->video->playAddr }}"
|
||||||
<img data-video_url="{{ $item->video->playAddr }}"
|
|
||||||
data-video_download="{{ $item->video->downloadAddr }}"
|
data-video_download="{{ $item->video->downloadAddr }}"
|
||||||
data-desc="{{ $item->desc }}"
|
data-desc="{{ $item->desc }}"
|
||||||
data-video_width="{{ $item->video->width }}"
|
data-video_width="{{ $item->video->width }}"
|
||||||
data-video_height="{{ $item->video->height }}"
|
data-video_height="{{ $item->video->height }}"
|
||||||
data-music_title="{{ $item->music->title }}"
|
data-music_title="{{ $item->music->title }}"
|
||||||
data-music_url="{{ $item->music->playUrl }}"
|
data-music_url="{{ $item->music->playUrl }}">
|
||||||
src="{{ $item->video->originCover }}"/>
|
<img src="{{ $item->video->originCover }}"/>
|
||||||
</figure>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<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)
|
@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
|
@else
|
||||||
<a class="button is-disabled">Next page</a>
|
<a class="button is-success" disabled title="No more videos available">Next</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -47,17 +49,21 @@
|
||||||
<div id="modal-background" class="modal-background"></div>
|
<div id="modal-background" class="modal-background"></div>
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
<p class="modal-card-title" id="item_title"></p>
|
|
||||||
<button id="modal-close" class="delete" aria-label="close"></button>
|
<button id="modal-close" class="delete" aria-label="close"></button>
|
||||||
|
<p class="modal-card-title" id="item_title"></p>
|
||||||
</header>
|
</header>
|
||||||
<section class="modal-card-body has-text-centered" style="overflow: hidden;">
|
<section class="modal-card-body has-text-centered" style="overflow: hidden;">
|
||||||
<video id="video" controls preload="none"></video>
|
<video id="video" controls preload="none"></video>
|
||||||
</section>
|
</section>
|
||||||
<footer class="modal-card-footer has-text-centered">
|
<footer class="modal-card-foot has-text-centered">
|
||||||
<div class="box">
|
<div class="container">
|
||||||
<a id="download_button" target="_blank" class="button is-info" href="#" download>Download</a>
|
<a id="download_button" target="_blank" class="button is-info" href="" download>Download</a>
|
||||||
<p id="audio_title"></p>
|
<p id="audio_title"></p>
|
||||||
<audio id="audio" controls></audio>
|
<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>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue