再生リスト
このコミットが含まれているのは:
コミット
48844f985b
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Watch;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Http\Controllers\Common;
|
||||||
|
// use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class Playlist extends Common {
|
||||||
|
private $common;
|
||||||
|
|
||||||
|
public function __construct () {
|
||||||
|
$this->common = new Common;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index ($id, Request $r) {
|
||||||
|
$res = [
|
||||||
|
'page' => 'playlist',
|
||||||
|
'style' => 'watch',
|
||||||
|
'id' => $id,
|
||||||
|
'pos' => isset($r->playlistPosition) ? (int)$r->playlistPosition : 1,
|
||||||
|
'resume' => isset($r->resume) ? (bool)$r->resume : true,
|
||||||
|
'userinfo' => $this->common->user,
|
||||||
|
];
|
||||||
|
|
||||||
|
$det = $this->getDetail($id);
|
||||||
|
$res['playlist'] = $det;
|
||||||
|
$res['plvid'] = $this->getVideos($det->uuid, 0, $det->videosLength);
|
||||||
|
|
||||||
|
if ($res['pos'] < 1) $res['pos'] = 1;
|
||||||
|
if ($res['pos'] > $det->videosLength) $res['pos'] = $det->videosLength;
|
||||||
|
|
||||||
|
$id = 0;
|
||||||
|
foreach ($res['plvid']->data as $k => $v) {
|
||||||
|
if ($v->position == $res['pos']) {
|
||||||
|
$id = $v->video->uuid;
|
||||||
|
$res['detail'] = $this->getVideo($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res['plvid']->data[$k]->video->nameShort = strlen($res['plvid']->data[$k]->video->name) > 45 ? substr($res['plvid']->data[$k]->video->name, 0, 45).'...' : $res['plvid']->data[$k]->video->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $this->getComment($id, $res);
|
||||||
|
|
||||||
|
$tags = [];
|
||||||
|
if (!is_null($res['detail']->tags)) $tags = $res['detail']->tags;
|
||||||
|
else $tags = explode(' ', $res['detail']->title);
|
||||||
|
|
||||||
|
$res['recommend'] = $this->getRecommend($tags);
|
||||||
|
return view('pages.peertube.w.p', ['res' => $res]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDetail ($id) {
|
||||||
|
return $this->ptapi('/api/v1/video-playlists/'.$id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideos ($uuid, $start, $count) {
|
||||||
|
return $this->ptapi('/api/v1/video-playlists/'.$uuid.'/videos?start='.$start.'&count='.$count);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideo ($uuid) {
|
||||||
|
return $this->ptapi('/api/v1/videos/'.$uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRecommend ($tags) {
|
||||||
|
$tag = '';
|
||||||
|
foreach ($tags as $t) {
|
||||||
|
$tag .= 'tagsOneOf='.urlencode($t).'&';
|
||||||
|
}
|
||||||
|
return $this->ptapi('/api/v1/search/videos?start=0&count=6&nsfw=both&'.$tag.'sort=-publishedAt&searchTarget=local');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComment ($id, $res) {
|
||||||
|
$get = null;
|
||||||
|
|
||||||
|
$res['comment'] = $this->ptapi('/api/v1/videos/'.$id.'/comment-threads');
|
||||||
|
foreach ($res['comment']->data as $co) {
|
||||||
|
$co->src = 'PT';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,3 +10,184 @@
|
||||||
.avatar-and-textarea my-actor-avatar {
|
.avatar-and-textarea my-actor-avatar {
|
||||||
margin-inline-end: 10px;
|
margin-inline-end: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.playlist {
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 470px;
|
||||||
|
height: 66vh;
|
||||||
|
background-color: var(--mainBackgroundColor);
|
||||||
|
overflow-y: auto;
|
||||||
|
border-bottom: 1px solid rgba(0,0,0,.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info {
|
||||||
|
padding: 5px 30px;
|
||||||
|
background-color: #31363b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info .playlist-display-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info .playlist-by-index {
|
||||||
|
color: var(--greyForegroundColor);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info .playlist-by-index .playlist-by {
|
||||||
|
margin-inline-end: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info .playlist-controls {
|
||||||
|
display: flex;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info .playlist-controls my-global-icon:not(:last-child) {
|
||||||
|
margin-inline-end: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info .playlist-controls my-global-icon:not(.active) {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist .playlist-info .playlist-controls my-global-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video {
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,.1);
|
||||||
|
color: var(--mainForegroundColor);
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video.active {
|
||||||
|
background-color: var(--mainColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video .position {
|
||||||
|
margin-inline-end: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--greyForegroundColor);
|
||||||
|
min-width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist my-video-playlist-element-miniature .video .position {
|
||||||
|
margin-inline-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
my-video-thumbnail, .fake-thumbnail {
|
||||||
|
margin-inline-end: 10px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist my-video-playlist-element-miniature my-video-thumbnail .video-thumbnail {
|
||||||
|
width: 90px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
my-video-thumbnail .video-thumbnail {
|
||||||
|
width: 130px;
|
||||||
|
height: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail-loli img {
|
||||||
|
width: 90px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail-label-overlay {
|
||||||
|
position: absolute;
|
||||||
|
padding: 0 5px;
|
||||||
|
left: 5px;
|
||||||
|
top: 5px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail-duration-overlay, .video-thumbnail-live-overlay {
|
||||||
|
position: absolute;
|
||||||
|
padding: 0 3px;
|
||||||
|
right: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail-watch-later-overlay, .video-thumbnail-label-overlay, .video-thumbnail-duration-overlay, .video-thumbnail-live-overlay {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #000000b3;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.1;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail .play-overlay .icon {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%,-50%) scale(.5);
|
||||||
|
border-top: 13px solid transparent;
|
||||||
|
border-bottom: 13px solid transparent;
|
||||||
|
border-left: 18px solid rgba(255,255,255,.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail .play-overlay, .video-thumbnail .play-overlay .icon {
|
||||||
|
transition: all .2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail .play-overlay {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
opacity: 0;
|
||||||
|
background-color: #0000004d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video .video-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-self: flex-start;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video .video-info .video-info-header {
|
||||||
|
display: flex;
|
||||||
|
align-self: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video .video-info .video-info-header a {
|
||||||
|
color: #fcfcfc;
|
||||||
|
width: auto;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist my-video-playlist-element-miniature .video .video-info .video-info-name {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video .video-info-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video .video-info .video-info-account, .video .video-info .video-info-timestamp {
|
||||||
|
color: var(--greyForegroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
my-global-icon {
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div id="video-wrapper">
|
||||||
|
<div id="videojs-wrapper">
|
||||||
|
<div id="vjs_video_3"
|
||||||
|
playsinline="true"
|
||||||
|
tabindex="-1"
|
||||||
|
lang="ja"
|
||||||
|
role="region"
|
||||||
|
aria-label="動画プレーヤー"
|
||||||
|
translate="no"
|
||||||
|
style="outline: none;"
|
||||||
|
>
|
||||||
|
<video
|
||||||
|
playsinline="playsinline"
|
||||||
|
tabindex="-1"
|
||||||
|
poster="{{ env('PEER_URI') }}{{ $res['detail']->previewPath }}"
|
||||||
|
style="width: 100%; max-height: 720px;"
|
||||||
|
controls=""
|
||||||
|
>
|
||||||
|
@if (!empty($res['detail']->streamingPlaylists))
|
||||||
|
@foreach ($res['detail']->streamingPlaylists[0]->files as $k => $v)
|
||||||
|
<source src="{{ $v->fileUrl }}">
|
||||||
|
@endforeach
|
||||||
|
@else
|
||||||
|
@foreach ($res['detail']->files as $k => $v)
|
||||||
|
<source src="{{ $v->fileUrl }}">
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@include('layout.component.w.playlist.watchplaylist')
|
||||||
|
</div>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="playlist-info">
|
||||||
|
<div class="playlist-display-name">
|
||||||
|
{{ $res['playlist']->displayName }}
|
||||||
|
<span class="badge badge-info">{{ ptPrivacy($res['playlist']->privacy->id) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="playlist-by-index">
|
||||||
|
<div class="playlist-by">{{ $res['playlist']->videoChannel->displayName }}</div>
|
||||||
|
<div class="playlist-index">
|
||||||
|
{{ $res['pos'] }}/{{ $res['playlist']->videosLength }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,35 @@
|
||||||
|
@foreach ($res['plvid']->data as $p)
|
||||||
|
<div class="element-{{ $p->position }}">
|
||||||
|
<my-video-playlist-element-miniature>
|
||||||
|
<div class="video{{ $p->position == $res['pos'] ? ' active' : '' }}">
|
||||||
|
<span style="margin: 4px;">{{ $p->position }}</span>
|
||||||
|
<my-video-thumbnail>
|
||||||
|
<a class="video-thumbnail-loli" href="/w/p/{{ $res['id'] }}?playlistPosition={{ $p->position }}&resume=true">
|
||||||
|
<img alt="" aria-label="{{ $p->video->name }}" src="{{ env('PEER_URI') }}{{ $p->video->thumbnailPath }}" />
|
||||||
|
<div class="video-thumbnail-duration-overlay">{{ ptDuration($p->video->duration) }}</div>
|
||||||
|
</a>
|
||||||
|
</my-video-thumbnail>
|
||||||
|
<div class="video-info">
|
||||||
|
<div class="video-info-header">
|
||||||
|
<a tabindex="-1" class="video-info-name" title="{{ $p->video->name }}" href="/w/p/{{ $res['id'] }}?playlistPosition={{ $p->position }}&resume=true">
|
||||||
|
{{ $p->video->nameShort }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span class="video-miniature-created-at-views">
|
||||||
|
<my-date-toggle>
|
||||||
|
<span class="date-toggle" title="{{ date('Y/m/d', strtotime($p->video->originallyPublishedAt)) }}">{{ date('Y年m月d日', strtotime($p->video->originallyPublishedAt)) }}</span>
|
||||||
|
</my-date-toggle>
|
||||||
|
<span class="views" title="">
|
||||||
|
•
|
||||||
|
<my-video-views-counter>
|
||||||
|
<span title="">{{ $p->video->views }} 回再生</span>
|
||||||
|
</my-video-views-counter>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span tabindex="-1" class="video-info-account">{{ $p->video->channel->displayName }}</span>
|
||||||
|
<span tabindex="-1" class="video-info-timestamp"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</my-video-playlist-element-miniature>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
|
@ -0,0 +1,6 @@
|
||||||
|
<my-video-watch-playlist class="playlist">
|
||||||
|
<div class="playlist">
|
||||||
|
@include('layout.component.w.playlist.components.info')
|
||||||
|
@include('layout.component.w.playlist.components.miniature')
|
||||||
|
</div>
|
||||||
|
</my-video-watch-playlist>
|
|
@ -0,0 +1,14 @@
|
||||||
|
@extends('layout')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div id="content" tabindex="-1" class="main-col">
|
||||||
|
<div class="main-row">
|
||||||
|
<my-video-watch>
|
||||||
|
<div class="root">
|
||||||
|
@include('layout.component.w.playlist')
|
||||||
|
@include('layout.component.w.info')
|
||||||
|
</div>
|
||||||
|
</my-video-watch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -7,7 +7,11 @@ Route::get('/home', 'Home@index');
|
||||||
|
|
||||||
Route::get('/a/{id}/{cat?}/{page?}', 'Account@index');
|
Route::get('/a/{id}/{cat?}/{page?}', 'Account@index');
|
||||||
Route::get('/c/{id}/{cat?}/{page?}', 'Channel@index');
|
Route::get('/c/{id}/{cat?}/{page?}', 'Channel@index');
|
||||||
Route::get('/w/{id}', 'Watch@index');
|
|
||||||
|
Route::group(['prefix' => 'w'], function () {
|
||||||
|
Route::get('/{id}', 'Watch@index');
|
||||||
|
Route::get('/p/{id}', 'Watch\Playlist@index');
|
||||||
|
});
|
||||||
|
|
||||||
Route::get('/logout', 'Logout@logout');
|
Route::get('/logout', 'Logout@logout');
|
||||||
Route::group(['prefix' => 'login'], function () {
|
Route::group(['prefix' => 'login'], function () {
|
||||||
|
|
新しいイシューから参照