Download services, scraper bump

This commit is contained in:
Pablo Ferreiro 2022-09-25 19:53:00 +02:00
parent c076ba65a6
commit 7aa869f567
No known key found for this signature in database
GPG key ID: 41FBCE65B779FA24
27 changed files with 191 additions and 73 deletions

View file

@ -1,7 +1,7 @@
<?php <?php
namespace App\Cache; namespace App\Cache;
use TikScraper\CacheInterface; use TikScraper\Interfaces\CacheInterface;
class JSONCache implements CacheInterface { class JSONCache implements CacheInterface {
private string $cache_path = __DIR__ . '/../../cache/api'; private string $cache_path = __DIR__ . '/../../cache/api';

View file

@ -1,7 +1,7 @@
<?php <?php
namespace App\Cache; namespace App\Cache;
use TikScraper\CacheInterface; use TikScraper\Interfaces\CacheInterface;
class RedisCache implements CacheInterface { class RedisCache implements CacheInterface {
private \Redis $client; private \Redis $client;

7
app/Constants/Themes.php Normal file
View file

@ -0,0 +1,7 @@
<?php
namespace App\Constants;
abstract class Themes {
const DEFAULT = "default";
const CARD = "card";
}

View file

@ -1,6 +1,9 @@
<?php <?php
namespace App\Controllers; namespace App\Controllers;
use App\Helpers\Cookies;
use TikScraper\Helpers\Converter;
class ProxyController { class ProxyController {
const VALID_TIKTOK_DOMAINS = [ const VALID_TIKTOK_DOMAINS = [
"tiktokcdn.com", "tiktokcdn-us.com", "tiktok.com" "tiktokcdn.com", "tiktokcdn-us.com", "tiktok.com"
@ -30,11 +33,9 @@ class ProxyController {
} }
static private function getFileName(): string { static private function getFilename(string $url, string $user): string {
$filename = 'tiktok-video'; $id = Converter::urlToId($url);
if (isset($_GET['user'])) { $filename = 'tiktok-video-' . $id . '-' . $user;
$filename .= '-' . $_GET['user'] . '-' . $_GET['id'];
}
return $filename; return $filename;
} }
@ -46,18 +47,17 @@ class ProxyController {
} }
static public function download() { static public function download() {
$downloader = new \TikScraper\Download();
$watermark = isset($_GET['watermark']);
if ($watermark) {
self::checkUrl(); self::checkUrl();
$filename = self::getFileName(); $method = Cookies::downloader();
$downloader->url($_GET['url'], $filename, true); $downloader = new \TikScraper\Download($method);
} else {
if (!isset($_GET['id'])) { // Params
die('You need to send an ID!'); $watermark = isset($_GET['watermark']);
} $url = $_GET['url'];
$filename = self::getFileName(); $user = $_GET['user'] ?? '';
$downloader->url($_GET['id'], $filename, false); // Filename
} $filename = self::getFilename($url, $user);
// Running
$downloader->url($_GET['url'], $filename, $watermark);
} }
} }

View file

@ -10,7 +10,7 @@ class RedirectController {
static public function redirect() { static public function redirect() {
$endpoint = '/'; $endpoint = '/';
if (isset($_GET['type'], $_GET['term'])) { if (isset($_GET['type'], $_GET['term'])) {
$term = trim($_GET['term']); $term = urlencode(trim($_GET['term']));
switch ($_GET['type']) { switch ($_GET['type']) {
case 'url': case 'url':
$endpoint = self::to_endpoint($term); $endpoint = self::to_endpoint($term);

View file

@ -4,12 +4,12 @@ namespace App\Controllers;
use App\Helpers\Misc; use App\Helpers\Misc;
use App\Helpers\Cookies; use App\Helpers\Cookies;
use App\Helpers\Wrappers; use App\Helpers\Wrappers;
use App\Models\BaseTemplate; use App\Models\SettingsTemplate;
class SettingsController { class SettingsController {
static public function index() { static public function index() {
$latte = Wrappers::latte(); $latte = Wrappers::latte();
$latte->render(Misc::getView('settings'), new BaseTemplate('Settings')); $latte->render(Misc::getView('settings'), new SettingsTemplate());
} }
static public function general() { static public function general() {
@ -25,6 +25,11 @@ class SettingsController {
$test_endpoints = $_POST['api-test_endpoints']; $test_endpoints = $_POST['api-test_endpoints'];
Cookies::set('api-test_endpoints', $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(); self::redirect();
} }

View file

@ -16,7 +16,7 @@ class TagController {
if ($hashtag->ok()) { if ($hashtag->ok()) {
$data = $hashtag->getFull(); $data = $hashtag->getFull();
$latte = Wrappers::latte(); $latte = Wrappers::latte();
$latte->render(Misc::getView('tag'), new FullTemplate('Tag', $data)); $latte->render(Misc::getView('tag'), new FullTemplate($data->info->detail->title, $data));
} else { } else {
ErrorHandler::showMeta($hashtag->error()); ErrorHandler::showMeta($hashtag->error());
} }

View file

@ -1,9 +1,9 @@
<?php <?php
namespace App\Helpers; namespace App\Helpers;
class Cookies { use App\Constants\Themes;
const ALLOWED_THEMES = ['default', 'card'];
class Cookies {
static public function get(string $name, string $default_value = ''): string { static public function get(string $name, string $default_value = ''): string {
if (isset($_COOKIE[$name]) && !empty($_COOKIE[$name])) { if (isset($_COOKIE[$name]) && !empty($_COOKIE[$name])) {
return $_COOKIE[$name]; return $_COOKIE[$name];
@ -13,12 +13,19 @@ class Cookies {
static public function theme(): string { static public function theme(): string {
$theme = self::get('theme'); $theme = self::get('theme');
if ($theme && in_array($theme, self::ALLOWED_THEMES)) { $ref = new \ReflectionClass(Themes::class);
$themes = $ref->getConstants();
if ($theme && in_array($theme, $themes)) {
return $theme; return $theme;
} }
return 'default'; return 'default';
} }
static public function downloader(): string {
$downloader = self::get('api-downloader', 'default');
return $downloader;
}
static public function exists(string $name): bool { static public function exists(string $name): bool {
return isset($_COOKIE[$name]); return isset($_COOKIE[$name]);
} }

View file

@ -0,0 +1,27 @@
<?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, bool $watermark): string {
// {path('/download?url=' . urlencode($playAddr) . '&id=' . $id . '&user=' . $uniqueId) . '&watermark=1'}
$down_url = Misc::url('/download?url=' . urlencode($url) . '&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 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;
}
}

View file

@ -29,6 +29,23 @@ class Wrappers {
$latte->addFunction('theme', function(): string { $latte->addFunction('theme', function(): string {
return Cookies::theme(); return Cookies::theme();
}); });
// 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, bool $watermark): string {
return UrlBuilder::download($url, $username, $watermark);
});
// https://stackoverflow.com/a/36365553 // https://stackoverflow.com/a/36365553
$latte->addFunction('number', function (float $x) { $latte->addFunction('number', function (float $x) {
if($x > 1000) { if($x > 1000) {

View file

@ -0,0 +1,31 @@
<?php
namespace App\Models;
use App\Constants\Themes;
use App\Helpers\Cookies;
use TikScraper\Constants\DownloadMethods;
/**
* Base for templates with a feed
*/
class SettingsTemplate extends BaseTemplate {
public array $downloaders = [];
public array $themes = [];
public bool $isTestEndpoints = false;
public string $currentDownloader;
public string $currentTheme;
function __construct() {
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();
}
}

View file

@ -15,6 +15,9 @@
{include './icon.latte', icon: 'info', text: 'About'} {include './icon.latte', icon: 'info', text: 'About'}
</a> </a>
</div> </div>
<div class="navbar-end">
<a href="https://github.com/pablouser1/ProxiTok" class="navbar-item" target="_blank">Source</a>
</div>
</div> </div>
</nav> </nav>
<script src="{path('/scripts/navbar.js')}"></script> <script src="{path('/scripts/navbar.js')}"></script>

View file

@ -1,19 +1,31 @@
{embed '../form.latte', path: '/settings/api', method: 'POST', submit: true} {embed '../form.latte', path: '/settings/api', method: 'POST', submit: true}
{block fields} {block fields}
{var $use_endpoints = \App\Helpers\Cookies::check('api-test_endpoints', 'yes')}
<div class="field"> <div class="field">
<label class="label">Use test endpoints</label> <label class="label">Use test endpoints</label>
<div class="control"> <div class="control">
<label class="radio"> <label class="radio">
<input type="radio" name="api-test_endpoints" value="yes" n:attr="checked => $use_endpoints" /> <input type="radio" name="api-test_endpoints" value="yes" n:attr="checked => $isTestEndpoints" />
<span>Yes</span> <span>Yes</span>
</label> </label>
<label class="radio"> <label class="radio">
<input type="radio" name="api-test_endpoints" value="no" n:attr="checked => !$use_endpoints" /> <input type="radio" name="api-test_endpoints" value="no" n:attr="checked => !$isTestEndpoints" />
<span>No</span> <span>No</span>
</label> </label>
</div> </div>
<p class="help">This <i>may</i> help bypass rate limits</p> <p class="help">This <i>may</i> help bypass rate limits</p>
</div> </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} {/block}
{/embed} {/embed}

View file

@ -2,13 +2,15 @@
{block fields} {block fields}
<div class="field"> <div class="field">
<label class="label">Theme</label> <label class="label">Theme</label>
<div class="control">
<div class="select"> <div class="select">
<select name="theme"> <select name="theme">
<option hidden disabled selected value> -- Select an option -- </option> {foreach $themes as $theme}
<option value="default">Default</option> <option value="{$theme}" n:attr="selected => $currentTheme === $theme">{$theme|firstUpper}</option>
<option value="card">Card</option> {/foreach}
</select> </select>
</div> </div>
</div> </div>
</div>
{/block} {/block}
{/embed} {/embed}

View file

@ -3,18 +3,18 @@
<section class="section"> <section class="section">
<div class="columns is-multiline is-vcentered"> <div class="columns is-multiline is-vcentered">
{foreach $data->feed->items as $item} {foreach $data->feed->items as $item}
{do $share_url = 'https://tiktok.com/@' . $item->author->uniqueId . '/video/' . $item->id} {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)" <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_url="{url_stream($item->video->playAddr)}"
data-video_download_watermark="{path('/download?url=' . urlencode($item->video->playAddr) . '&id=' . $item->id . '&user=' . $item->author->uniqueId) . '&watermark='}" data-video_download_watermark="{url_download($item->video->playAddr, $item->author->uniqueId, true)}"
data-video_download_nowatermark="{path('/download?id=' . $item->id . '&user=' . $item->author->uniqueId)}" data-video_download_nowatermark="{url_download($item->video->playAddr, $item->author->uniqueId, false)}"
data-video_share_url="{$share_url}" data-video_share_url="{$share_url}"
data-desc="{$item->desc}" data-desc="{$item->desc}"
data-createtime="{$item->createTime}" data-createtime="{$item->createTime}"
data-music_title="{$item->music->title}" data-music_title="{$item->music->title}"
data-music_url="{path('/stream?url=' . urlencode($item->music->playUrl))}"> data-music_url="{url_stream($item->music->playUrl)}">
<img loading="lazy" src="{path('/stream?url=' . urlencode($item->video->originCover))}" /> <img loading="lazy" src="{url_stream($item->video->originCover)}" />
<img class="hidden" loading="lazy" data-src="{path('/stream?url=' . urlencode($item->video->dynamicCover))}" /> <img class="hidden" loading="lazy" data-src="{url_stream($item->video->dynamicCover)}" />
</div> </div>
{/foreach} {/foreach}
{if empty($data->feed->items)} {if empty($data->feed->items)}

View file

@ -6,8 +6,8 @@
</div> </div>
<div class="dropdown-menu" role="menu"> <div class="dropdown-menu" role="menu">
<div class="dropdown-content"> <div class="dropdown-content">
<a target="_blank" href="{path('/download?url=' . urlencode($playAddr) . '&id=' . $id . '&user=' . $uniqueId) . '&watermark=1'}" class="dropdown-item">Watermark</a> <a target="_blank" href="{url_download($playAddr, $uniqueId, false)}" class="dropdown-item">Watermark</a>
<a target="_blank" href="{path('/download?id=' . $id . '&user=' . $uniqueId)}" class="dropdown-item">No watermark</a> <a target="_blank" href="{url_download(url_video_external($uniqueId, $id), $uniqueId, true)}" class="dropdown-item">No watermark</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,5 +1,5 @@
{do $endpoint = '/@' . $uniqueId . '/video/' . $id} {do $endpoint = '/@' . $uniqueId . '/video/' . $id}
<div class="buttons is-centered"> <div class="buttons is-centered">
<a class="button is-success is-small" href="{path($endpoint)}">Instance Link</a> <a class="button is-success is-small" href="{url_video_internal($uniqueId, $id)}">Instance Link</a>
<a class="button is-danger is-small" href="https://www.tiktok.com{$endpoint}">Original Link</a> <a class="button is-danger is-small" href="{url_video_external($uniqueId, $id)}">Original Link</a>
</div> </div>

View file

@ -3,7 +3,7 @@
<article class="media"> <article class="media">
<figure class="media-left"> <figure class="media-left">
<p class="image is-64x64"> <p class="image is-64x64">
<img src="{path('/stream?url=' . urlencode($item->author->avatarThumb))}" /> <img src="{url_stream($item->author->avatarThumb)}" />
</p> </p>
</figure> </figure>
<div class="media-content"> <div class="media-content">
@ -11,7 +11,7 @@
<p> <p>
<strong>{$item->author->nickname}</strong> <strong>{$item->author->nickname}</strong>
<small> <small>
<a href="{path('/@' . $item->author->uniqueId)}">@{$item->author->uniqueId}</a> <a href="{url_user($item->author->uniqueId)}">@{$item->author->uniqueId}</a>
</small> </small>
<small title="{date('M d, Y H:i:s e', $item->createTime)}">{date('M d, Y', $item->createTime)}</small> <small title="{date('M d, Y H:i:s e', $item->createTime)}">{date('M d, Y', $item->createTime)}</small>
</p> </p>
@ -22,8 +22,8 @@
{include './common/stats.latte', playCount: $item->stats->playCount, diggCount: $item->stats->diggCount, commentCount: $item->stats->commentCount, shareCount: $item->stats->shareCount} {include './common/stats.latte', playCount: $item->stats->playCount, diggCount: $item->stats->diggCount, commentCount: $item->stats->commentCount, shareCount: $item->stats->shareCount}
</div> </div>
<div class="has-text-centered"> <div class="has-text-centered">
<video width="{$item->video->width}" height="{$item->video->height}" controls preload="none" poster="{path('/stream?url=' . urlencode($item->video->originCover))}"> <video width="{$item->video->width}" height="{$item->video->height}" controls preload="none" poster="{url_stream($item->video->originCover)}">
<source src="{path('/stream?url=' . $item->video->playAddr)}" type="video/mp4" /> <source src="{url_stream($item->video->playAddr)}" type="video/mp4" />
</video> </video>
</div> </div>
<div class="has-text-centered"> <div class="has-text-centered">

View file

@ -1,7 +1,7 @@
{ {
"name": "pablouser1/proxitok", "name": "pablouser1/proxitok",
"description": "An alternative frontend for TikTok", "description": "An alternative frontend for TikTok",
"version": "2.4.2.0", "version": "2.4.2.1",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"type": "project", "type": "project",
"authors": [ "authors": [
@ -24,7 +24,7 @@
"latte/latte": "^2.11", "latte/latte": "^2.11",
"bramus/router": "^1.6", "bramus/router": "^1.6",
"josegonzalez/dotenv": "dev-master", "josegonzalez/dotenv": "dev-master",
"pablouser1/tikscraper": "^2.2" "pablouser1/tikscraper": "^2.3"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

17
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "ec01a866b0314de1e8345a625b066075", "content-hash": "0e42a826ea58a0a415290531bd89cd2d",
"packages": [ "packages": [
{ {
"name": "bramus/router", "name": "bramus/router",
@ -263,16 +263,16 @@
}, },
{ {
"name": "pablouser1/tikscraper", "name": "pablouser1/tikscraper",
"version": "v2.2.1.1", "version": "v2.3.0.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pablouser1/TikScraperPHP.git", "url": "https://github.com/pablouser1/TikScraperPHP.git",
"reference": "535ed6db108c9a0ec41b271bea8a6a10700cc5d7" "reference": "b7d890ea20b24baa4d5a2c3874575027982ad3a4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pablouser1/TikScraperPHP/zipball/535ed6db108c9a0ec41b271bea8a6a10700cc5d7", "url": "https://api.github.com/repos/pablouser1/TikScraperPHP/zipball/b7d890ea20b24baa4d5a2c3874575027982ad3a4",
"reference": "535ed6db108c9a0ec41b271bea8a6a10700cc5d7", "reference": "b7d890ea20b24baa4d5a2c3874575027982ad3a4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -298,15 +298,16 @@
], ],
"authors": [ "authors": [
{ {
"name": "Pablo Ferreiro" "name": "Pablo Ferreiro",
"homepage": "https://pabloferreiro.es"
} }
], ],
"description": "Get data from TikTok API", "description": "Get data from TikTok API",
"support": { "support": {
"issues": "https://github.com/pablouser1/TikScraperPHP/issues", "issues": "https://github.com/pablouser1/TikScraperPHP/issues",
"source": "https://github.com/pablouser1/TikScraperPHP/tree/v2.2.1.1" "source": "https://github.com/pablouser1/TikScraperPHP/tree/v2.3.0.1"
}, },
"time": "2022-09-03T11:19:59+00:00" "time": "2022-09-25T17:03:08+00:00"
}, },
{ {
"name": "php-webdriver/webdriver", "name": "php-webdriver/webdriver",

1
scss/bulma.scss vendored
View file

@ -10,6 +10,7 @@ $bulmaswatch-import-font: false;
@import "./node_modules/bulma/sass/base/_all.sass"; @import "./node_modules/bulma/sass/base/_all.sass";
// Elements // Elements
@import "./node_modules/bulma/sass/elements/box.sass";
@import "./node_modules/bulma/sass/elements/button.sass"; @import "./node_modules/bulma/sass/elements/button.sass";
@import "./node_modules/bulma/sass/elements/container.sass"; @import "./node_modules/bulma/sass/elements/container.sass";
@import "./node_modules/bulma/sass/elements/icon.sass"; @import "./node_modules/bulma/sass/elements/icon.sass";

View file

@ -13,3 +13,6 @@
@import "./node_modules/css.gg/icons/scss/comment.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/share.scss";
@import "./node_modules/css.gg/icons/scss/software-download.scss"; @import "./node_modules/css.gg/icons/scss/software-download.scss";
// Video
@import "./node_modules/css.gg/icons/scss/music.scss";

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../scss/node_modules/css.gg/icons/scss/home.scss","../../scss/node_modules/css.gg/icons/scss/info.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"],"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,SACI,sBACA,kBACA,cACA,+BACA,WACA,YACA,iBACA,mBAEA,iCACE,WACA,cACA,sBACA,kBACA,kBACA,UACA,wBACA,SAGF,gBACE,WACA,WAGF,iBACE,WACA,QC5BN,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","file":"cssgg.min.css"} {"version":3,"sourceRoot":"","sources":["../../scss/node_modules/css.gg/icons/scss/home.scss","../../scss/node_modules/css.gg/icons/scss/info.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/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,SACI,sBACA,kBACA,cACA,+BACA,WACA,YACA,iBACA,mBAEA,iCACE,WACA,cACA,sBACA,kBACA,kBACA,UACA,wBACA,SAGF,gBACE,WACA,WAGF,iBACE,WACA,QC5BN,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,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"}

View file

@ -1,22 +1,24 @@
{layout "../layouts/{$layout}.latte"} {layout "../layouts/{$layout}.latte"}
{block content} {block content}
<div class="columns is-centered is-vcentered is-gapless"> <div class="columns has-text-centered is-centered is-vcentered is-gapless">
<div class="column is-three-quarters"> <div class="column">
<video controls autoplay playsinline poster="{path('/stream?url=' . urlencode($item->video->originCover))}"> <video controls autoplay poster="{url_stream($item->video->originCover)}">
<source src="{path('/stream?url=' . urlencode($item->video->playAddr))}" type="video/mp4" /> <source src="{url_stream($item->video->playAddr)}" type="video/mp4" />
</video> </video>
</div> </div>
<div class="column has-text-centered"> <div class="column">
<div class="box"> <div class="box">
<p class="title">Video by <a href="{path('/@'.$detail->uniqueId)}">{$detail->uniqueId}</a></p> <p class="title">Video by <a href="{url_user($detail->uniqueId)}">{$detail->uniqueId}</a></p>
<p class="subtitle">{$item->desc}</p> <p class="subtitle">{$item->desc}</p>
{include '../components/themes/common/stats.latte', playCount: $item->stats->playCount, diggCount: $item->stats->diggCount, commentCount: $item->stats->commentCount, shareCount: $item->stats->shareCount} {include '../components/themes/common/stats.latte', playCount: $item->stats->playCount, diggCount: $item->stats->diggCount, commentCount: $item->stats->commentCount, shareCount: $item->stats->shareCount}
<div class="container mt-4">
<p>{include '../components/icon.latte', icon: 'music', text: $item->music->title}</p>
<audio src="{url_stream($item->music->playUrl)}" controls preload="none"></audio>
</div>
<hr /> <hr />
{include '../components/themes/common/share.latte', uniqueId: $detail->uniqueId, id: $item->id} {include '../components/themes/common/share.latte', uniqueId: $detail->uniqueId, id: $item->id}
{include '../components/themes/common/download.latte', playAddr: $item->video->playAddr, id: $item->id, uniqueId: $detail->uniqueId} {include '../components/themes/common/download.latte', playAddr: $item->video->playAddr, id: $item->id, uniqueId: $detail->uniqueId}
<p>{$item->music->title}</p>
<audio src="{path('/stream?url=' . urlencode($item->music->playUrl))}" controls preload="none"></audio>
</div> </div>
</div> </div>
</div> </div>