Initial commit

This commit is contained in:
vsphim 2026-05-20 11:52:46 +07:00
commit c8142f6b7e
36 changed files with 1868 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

60
README.md Normal file
View File

@ -0,0 +1,60 @@
# THEME - VUNG 2022 - VSMOV CMS
## Demo
### Trang Chủ
![Alt text](https://i.ibb.co/J7rX36z/THEME-VUNG-INDEX.png "Home Page")
### Trang Danh Sách Phim
![Alt text](https://i.ibb.co/hKRW8kp/THEME-VUNG-CATALOG.png "Catalog Page")
### Trang Thông Tin Phim
![Alt text](https://i.ibb.co/7WN0gD7/THEME-VUNG-SINGLE.png "Info Page")
### Trang Xem Phim
![Alt text](https://i.ibb.co/HpSJg0y/THEME-VUNG-EPISODE.png "Episode Page")
## Requirements
https://github.com/vsphim/vsmov-core
## Install
1. Tại thư mục của Project: `composer require vsmov/theme-vung`
2. Kích hoạt giao diện trong Admin Panel
## Update
1. Tại thư mục của Project: `composer update vsmov/theme-vung`
2. Re-Activate giao diện trong Admin Panel
## Note
- Một vài lưu ý quan trọng của các nút chức năng:
+ `Activate``Re-Activate` sẽ publish toàn bộ file js,css trong themes ra ngoài public của laravel.
+ `Reset` reset lại toàn bộ cấu hình của themes
## Document
### List
- Trang chủ: `display_label|relation|find_by_field|value|limit|show_more_url`
```
Phim chiếu rạp||is_shown_in_theater|1|updated_at|desc|12|/danh-sach/phim-chieu-rap
Phim bộ mới||type|series|updated_at|desc|12|/danh-sach/phim-bo
Phim lẻ mới||type|single|updated_at|desc|12|/danh-sach/phim-le
Hoạt hình|categories|slug|hoat-hinh|updated_at|desc|12|/the-loai/hoat-hinh
```
- Top: `Label|relation|find_by_field|value|sort_by_field|sort_algo|limit|show_template (top_thumb|top_poster|top_poster_small)`
```
Phim sắp chiếu||type|single|view_week|desc|10|top_poster_small
Top phim lẻ||type|single|view_week|desc|5|top_thumb
Top phim bộ||type|series|view_week|desc|10|top_poster
```
### Custom View Blade
- File blade gốc trong Package: `/vendor/vsmov/theme-vung/resources/views/themevung`
- Copy file cần custom đến: `/resources/views/vendor/themes/themevung`

32
composer.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "vsmov/theme-vung",
"description": "VsMov's vung theme",
"type": "library",
"authors": [
{
"name": "vsmov",
"email": "vsmov@gmail.com"
}
],
"require": {
"laravel/framework": "^6|^7|^8",
"ckfinder/ckfinder-laravel-package": "v3.5.2.1",
"vsphim/vsmov-core": "^1.0.1"
},
"license": "MIT",
"autoload": {
"psr-4": {
"VsMov\\ThemeVung\\": "src/",
"VsMov\\ThemeVung\\Database\\Factories\\": "database/factories/",
"VsMov\\ThemeVung\\Database\\Seeders\\": "database/seeders/"
}
},
"extra": {
"laravel": {
"providers": [
"VsMov\\ThemeVung\\ThemeVungServiceProvider"
]
}
},
"minimum-stability": "stable"
}

20
resources/assets/css/v.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="75px" height="75px" viewBox="0 0 75 75" enable-background="new 0 0 75 75" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#D2D2D2" d="M68,0H7C3.134,0,0,3.134,0,7v61c0,3.866,3.134,7,7,7h61
c3.866,0,7-3.134,7-7V7C75,3.134,71.866,0,68,0z M58,41.065c0,0.492-0.399,0.891-0.891,0.891H41.957v15.152
c0,0.492-0.399,0.891-0.891,0.891h-7.13c-0.492,0-0.891-0.399-0.891-0.891V41.956H17.891c-0.492,0-0.891-0.399-0.891-0.891v-7.13
c0-0.492,0.399-0.891,0.891-0.891h15.152V17.891c0-0.492,0.399-0.891,0.891-0.891h7.13c0.492,0,0.891,0.399,0.891,0.891v15.152
h15.152c0.492,0,0.891,0.399,0.891,0.891V41.065z"/>
<g id="Livello_1_1_">
</g>
</svg>

After

Width:  |  Height:  |  Size: 1023 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,24 @@
$(document).on('click', '#but_send_report', function () {
var log_des = $('#log_des').val() ?? '';
$.ajax({
url: URL_POST_REPORT,
method: 'POST',
headers: {
"Content-Type": "application/json",
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
'content')
},
dataType: 'json',
data: JSON.stringify({ message: log_des })
}).done(function (data) {
var str = "Gửi báo lỗi thành công!";
setTimeout(function () {
$('#ModalBaoloi').modal('hide');
}, 1000);
$('#show_msg').html(str);
$("#ModalBaoloi").modal();
$('#report_error').remove();
}).fail(function () {
console.log('error-connection');
});
});

View File

@ -0,0 +1,76 @@
$(document).ready(function () {
if (typeof $(document).jRate != 'undefined' && $.isFunction($(document).jRate) && typeof $("#star") != 'undefined') {
var rated = $("#star").jRate({
rating: rating,
startColor: "#77C282",
endColor: "#77C282",
count: 10,
max: 10,
precision: 1,
readOnly: true,
width: 25,
height: 25,
shapeGap: '2px',
backgroundColor: '#888888',
});
$("#star-vote").jRate({
rating: 0,
startColor: "#77C282",
endColor: "#77C282",
count: 10,
max: 10,
precision: 1,
width: 25,
height: 25,
shapeGap: '10px',
backgroundColor: '#888888',
onSet: function (rating) {
$.ajax({
url: URL_POST_RATING,
type: 'POST',
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
},
data: JSON.stringify({
rating: rating,
}),
}).done(function (data) {
if (data.status == 1) {
refresh_rating = data.rating_star;
$("#star-vote").empty().jRate({
rating: rating,
count: 10,
max: 10,
precision: 1,
readOnly: true,
width: 25,
height: 25,
shapeGap: '10px',
});
$("#star").empty().jRate({
rating: refresh_rating,
startColor: "#77C282",
endColor: "#77C282",
count: 10,
max: 10,
precision: 1,
readOnly: true,
width: 25,
height: 25,
shapeGap: '2px',
backgroundColor: '#888888',
});
$('.rated-text').html('(' + data.rating_count + ' Đánh giá)');
} else {
console.log(data.message);
}
}).fail(function () {
console.log('error-connection');
});
}
});
}
});

View File

@ -0,0 +1,55 @@
$(document).ready(function () {
$('.play-trailer').on('click', function (e) {
e.preventDefault();
var youtubeId = $(this).attr('href').match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/);
if (youtubeId && youtubeId[1] != null && youtubeId[1] != 'undefined') {
var vidWidth = 560;
var vidHeight = 320;
if ($(this).attr('data-width')) {
vidWidth = parseInt($(this).attr('data-width'));
}
if ($(this).attr('data-height')) {
vidHeight = parseInt($(this).attr('data-height'));
}
var iFrameCode = '<iframe width="' + vidWidth + '" height="' + vidHeight + '" scrolling="no" allowtransparency="true" allowfullscreen="true" src="https://www.youtube.com/embed/' + youtubeId[1] + '?rel=0&wmode=transparent&showinfo=0&hd=1" frameborder="0"></iframe>';
$('#mediaModal .modal-body').html(iFrameCode);
$('#mediaModal').on('show.bs.modal', function () {
var modalBody = $(this).find('.modal-body');
var modalDialog = $(this).find('.modal-dialog');
var newModalWidth = vidWidth + parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right"));
newModalWidth += parseInt(modalDialog.css("padding-left")) + parseInt(modalDialog.css("padding-right"));
newModalWidth += 'px';
$(this).find('.modal-dialog').css('width', newModalWidth);
});
$('#mediaModal').modal();
}
});
$('.comingsoon').on('click', function (e) {
e.preventDefault();
var youtubeId = $('.play-trailer').attr('href')?.match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/);
if (youtubeId && youtubeId[1] != null && youtubeId[1] != 'undefined') {
var vidWidth = 560;
var vidHeight = 320;
if ($(this).attr('data-width')) {
vidWidth = parseInt($(this).attr('data-width'));
}
if ($(this).attr('data-height')) {
vidHeight = parseInt($(this).attr('data-height'));
}
var iFrameCode = '<iframe width="' + vidWidth + '" height="' + vidHeight + '" scrolling="no" allowtransparency="true" allowfullscreen="true" src="https://www.youtube.com/embed/' + youtubeId[1] + '?rel=0&wmode=transparent&showinfo=0&hd=1" frameborder="0"></iframe>';
$('#mediaModal .modal-body').html(iFrameCode);
$('#mediaModal').on('show.bs.modal', function () {
var modalBody = $(this).find('.modal-body');
var modalDialog = $(this).find('.modal-dialog');
var newModalWidth = vidWidth + parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right"));
newModalWidth += parseInt(modalDialog.css("padding-left")) + parseInt(modalDialog.css("padding-right"));
newModalWidth += 'px';
$(this).find('.modal-dialog').css('width', newModalWidth);
});
$('#mediaModal').modal();
}
});
$('#mediaModal').on('hidden.bs.modal', function () {
$('#mediaModal .modal-body').html('');
});
});

10
resources/assets/js/v.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,48 @@
@extends('themes::themevung.layout')
@php
$years = Cache::remember('all_years', \Backpack\Settings\app\Models\Setting::get('site_cache_ttl', 5 * 60), function () {
return \VsMov\Core\Models\Movie::select('publish_year')
->distinct()
->pluck('publish_year')
->sortDesc();
});
@endphp
@section('content')
<div class="path-folder-film" style="margin-top: 30px">
<ul itemscope itemtype="http://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
<a itemprop="item" itemprop="url" href="/" title="Xem phim">
<span itemprop="name"> <i class="fa fa-home"></i> Xem phim</span>
</a>
<i class="fa fa-angle-right"></i>
<meta itemprop="position" content="1" />
</li>
<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
<a itemprop="item" itemprop="url"
href="{{ url()->current() }}"
title="{{ $section_name ?? 'Danh Sách Phim' }}">
<span itemprop="name"> {{ $section_name ?? 'Danh Sách Phim' }}</span>
</a>
<i class="fa fa-angle-right"></i>
<meta itemprop="position" content="2" />
</li>
</ul>
</div>
<div class="group-film group-film-category">
<h1>{{$section_name}}<i class="fa fa-caret-right" aria-hidden="true"></i></h1> <span class="line-ngang"></span>
@include('themes::themevung.inc.catalog_filter')
<div class="row group-film-small">
@if (count($data))
@foreach ($data as $movie)
@include('themes::themevung.inc.movie_card')
@endforeach
@else
<p class="text-danger">Không dữ liệu cho mục này</p>
@endif
</div>
{{ $data->appends(request()->all())->links("themes::themevung.inc.pagination") }}
</div>
@endsection

View File

@ -0,0 +1,404 @@
@extends('themes::themevung.layout')
@section('content')
<div class="path-folder-film" style="margin-top: 20px">
<ul itemscope itemtype="http://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
<a itemprop="item" itemprop="url" href="/" title="Xem phim">
<span itemprop="name"> <i class="fa fa-home"></i> Xem phim</span>
</a>
<i class="fa fa-angle-right"></i>
<meta itemprop="position" content="1" />
</li>
<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
<a itemprop="item" itemprop="url" href="{{ $currentMovie->getUrl() }}" title="{{ $currentMovie->name }}">
<span itemprop="name"> {{ $currentMovie->name }}</span>
</a>
<i class="fa fa-angle-right"></i>
<meta itemprop="position" content="3" />
</li>
<li>
<a href="javascript:;"
class="active">{{ strpos(strtolower($episode->name), 'tập') ? $episode->name : "Tập $episode->name" }}</a>
</li>
</ul>
</div>
<div id="player-wrapper">
<div id="player-holder" class="player-film"></div>
<div class="buttonlight-film">
<ul>
<li>
<div class="fb-gg">
<div class="fb-like" data-href="{{ $currentMovie->getUrl() }}" data-layout="button_count"
data-action="like" data-size="small" data-show-faces="false" data-share="true">
</div>
<div class="g-plusone" data-size="medium"></div>
</div>
</li>
<li>
<a id="report_error" data-toggle="modal" data-target="#ModalBaoloi">
<i class="fa fa-exclamation-circle" aria-hidden="true"></i>Báo lỗi
</a>
</li>
@foreach ($currentMovie->episodes->where('slug', $episode->slug)->where('server', $episode->server) as $server)
<li>
<a data-id="{{ $server->id }}" data-link="{{ $server->link }}" data-type="{{ $server->type }}"
onclick="chooseStreamingServer(this)" class="streaming-server">
<i class="fa fa-play" aria-hidden="true"></i>
Nguồn #{{ $loop->index + 1 }}
</a>
</li>
@endforeach
<li>
<a class="light-on show1">Tắt đèn <i class="fa fa-lightbulb-o" aria-hidden="true"></i>
</a>
<a class="light-off">Bật đèn <i class="fa fa-lightbulb-o" aria-hidden="true"></i>
</a>
</li>
<li class="scene-dim"></li>
</ul>
</div>
</div>
<div class="alert alert-warning" role="alert"> Xem tốt nhất trên trình duyệt Chrome, Safari. Nếu
bạn không xem được phim, vui lòng <b>đổi nguồn phát</b> hoặc <b>đổi server</b>, chúc bạn xem phim
vui vẻ.
</div>
<div class="episode-film group-detail">
@if ($currentMovie->notify && $currentMovie->notify != '')
<p class="lichchieu"><span class="text-danger"> Thông báo</span>: {{ strip_tags($currentMovie->notify) }}</p>
@endif
@if ($currentMovie->showtimes && $currentMovie->showtimes != '')
<p class="lichchieu"><span class="text-info"> Lịch chiếu</span>: {{ strip_tags($currentMovie->showtimes) }}
</p>
@endif
@foreach ($currentMovie->episodes->sortBy([['server', 'asc']])->groupBy('server') as $server => $data)
<span class="text-success">{{ $server }}</span>
<div class="episode-main">
<ul>
@foreach ($data->sortByDesc('name', SORT_NATURAL)->groupBy('name') as $name => $item)
<li>
<a href="{{ $item->sortByDesc('type')->first()->getUrl() }}"
title="Xem phim {{ $currentMovie->name }} {{ $name }}"
class="@if ($item->contains($episode)) active @endif">
{{ $name }}
</a>
</li>
@endforeach
</ul>
</div>
@endforeach
</div>
<div class="title-film-film-0">
<a href="{{ $currentMovie->getUrl() }}" class="">
<h1 class="title-film-film-1">{{ $currentMovie->name }} -
{{ strpos(strtolower($name), 'tập') ? $name : 'Tập ' . $name }}</h1> <br>
<h2 class="title-film-film-2">{{ $currentMovie->origin_name }} ({{ $currentMovie->publish_year }})</h2>
</a>
</div>
<div class="group-detail ">
<div class="imdb">Điểm {{$currentMovie->getRatingStar()}}</div>
<ul class="rated-star hidden-xs">
<i id="star"></i>
</ul>
<span class="rated-text">({{$currentMovie->getRatingCount()}} Đánh giá)</span>
</div>
<div class="group-vote-detail">
<h2>Đánh giá phim này</h2>
<ul>
<li class="star" id="star-vote"></li>
</ul>
</div>
<div class="fbchat">
<div class="fb-comments" data-width="100%" data-include-parent="false" data-href="{{ $currentMovie->getUrl() }}"
data-numposts="10" data-order-by="reverse_time" data-colorscheme="dark"></div>
</div>
<div class="group-film">
<h2>phim liên quan <i class="fa fa-caret-right" aria-hidden="true"></i>
</h2>
<span class="line-ngang"></span>
<div class="group-film-small">
@foreach ($movie_related as $movie)
@include('themes::themevung.inc.movie_card')
@endforeach
</div>
</div>
@endsection
@push('scripts')
<div id="ModalBaoloi" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Thông báo</h4>
</div>
<div class="modal-body" id="p_content">
<p class="alert-danger" id="show_msg"></p>
<div class="form-group">
<label for="log_des">Báo lỗi phim "{{ $currentMovie->name }} ({{ $currentMovie->origin_name }}) ({{ $currentMovie->publish_year }})"</label>
<textarea name="log_des" id="log_des" class="form-control" style="width:100%; height: 50px;"
placeholder="Mô tả lỗi phim {{ $currentMovie->name }} ({{ $currentMovie->origin_name }}) ({{ $currentMovie->publish_year }})"></textarea>
</div>
<a href="javascript:;" class="btn btn-primary" name="but_send_report" id="but_send_report">Gửi</a>
</div>
</div>
</div>
</div>
<script>
var rating = '{{$currentMovie->getRatingStar()}}';
var URL_POST_RATING = '{{ route('movie.rating', ['movie' => $currentMovie->slug]) }}';
var URL_POST_REPORT = '{{ route('episodes.report', ['movie' => $currentMovie->slug, 'episode' => $episode->slug, 'id' => $episode->id]) }}';
</script>
<script defer type="text/javascript" src="/themes/vung/js/rating.js"></script>
<script defer type="text/javascript" src="/themes/vung/js/episode.js"></script>
<script src="/themes/vung/player/js/p2p-media-loader-core.min.js"></script>
<script src="/themes/vung/player/js/p2p-media-loader-hlsjs.min.js"></script>
<script src="/js/jwplayer-8.9.3.js"></script>
<script src="/js/hls.min.js"></script>
<script src="/js/jwplayer.hlsjs.min.js"></script>
<script>
var episode_id = {{ $episode->id }};
const wrapper = document.getElementById('player-holder');
const vastAds = "{{ Setting::get('jwplayer_advertising_file') }}";
function chooseStreamingServer(el) {
const type = el.dataset.type;
const link = el.dataset.link.replace(/^http:\/\//i, 'https://');
const id = el.dataset.id;
const newUrl =
location.protocol +
"//" +
location.host +
location.pathname.replace(`-${episode_id}`, `-${id}`);
history.pushState({
path: newUrl
}, "", newUrl);
episode_id = id;
Array.from(document.getElementsByClassName('streaming-server')).forEach(server => {
server.classList.remove('active');
})
el.classList.add('active');
link.replace('http://', 'https://');
renderPlayer(type, link, id);
}
function renderPlayer(type, link, id) {
if (type == 'embed') {
if (vastAds) {
wrapper.innerHTML = `<div id="fake_jwplayer"></div>`;
const fake_player = jwplayer("fake_jwplayer");
const objSetupFake = {
key: "{{ Setting::get('jwplayer_license') }}",
aspectratio: "16:9",
width: "100%",
file: "/themes/vung/player/1s_blank.mp4",
volume: 100,
mute: false,
autostart: true,
advertising: {
tag: "{{ Setting::get('jwplayer_advertising_file') }}",
client: "vast",
vpaidmode: "insecure",
skipoffset: {{ (int) Setting::get('jwplayer_advertising_skipoffset') ?: 5 }}, // Bỏ qua quảng cáo trong vòng 5 giây
skipmessage: "Bỏ qua sau xx giây",
skiptext: "Bỏ qua"
}
};
fake_player.setup(objSetupFake);
fake_player.on('complete', function(event) {
$("#fake_jwplayer").remove();
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
allowfullscreen="" allow='autoplay'></iframe>`
fake_player.remove();
});
fake_player.on('adSkipped', function(event) {
$("#fake_jwplayer").remove();
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
allowfullscreen="" allow='autoplay'></iframe>`
fake_player.remove();
});
fake_player.on('adComplete', function(event) {
$("#fake_jwplayer").remove();
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
allowfullscreen="" allow='autoplay'></iframe>`
fake_player.remove();
});
} else {
if (wrapper) {
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
allowfullscreen="" allow='autoplay'></iframe>`
}
}
return;
}
if (type == 'm3u8' || type == 'mp4') {
wrapper.innerHTML = `<div id="jwplayer"></div>`;
const player = jwplayer("jwplayer");
const objSetup = {
key: "{{ Setting::get('jwplayer_license') }}",
aspectratio: "16:9",
width: "100%",
image: "{{ $currentMovie->getPosterUrl() }}",
file: link,
playbackRateControls: true,
playbackRates: [0.25, 0.75, 1, 1.25],
sharing: {
sites: [
"reddit",
"facebook",
"twitter",
"googleplus",
"email",
"linkedin",
],
},
volume: 100,
mute: false,
autostart: true,
logo: {
file: "{{ Setting::get('jwplayer_logo_file') }}",
link: "{{ Setting::get('jwplayer_logo_link') }}",
position: "{{ Setting::get('jwplayer_logo_position') }}",
},
advertising: {
tag: "{{ Setting::get('jwplayer_advertising_file') }}",
client: "vast",
vpaidmode: "insecure",
skipoffset: {{ (int) Setting::get('jwplayer_advertising_skipoffset') ?: 5 }}, // Bỏ qua quảng cáo trong vòng 5 giây
skipmessage: "Bỏ qua sau xx giây",
skiptext: "Bỏ qua"
}
};
if (type == 'm3u8') {
const segments_in_queue = 50;
var engine_config = {
debug: !1,
segments: {
forwardSegmentCount: 50,
},
loader: {
cachedSegmentExpiration: 864e5,
cachedSegmentsCount: 1e3,
requiredSegmentsPriority: segments_in_queue,
httpDownloadMaxPriority: 9,
httpDownloadProbability: 0.06,
httpDownloadProbabilityInterval: 1e3,
httpDownloadProbabilitySkipIfNoPeers: !0,
p2pDownloadMaxPriority: 50,
httpFailedSegmentTimeout: 500,
simultaneousP2PDownloads: 20,
simultaneousHttpDownloads: 2,
// httpDownloadInitialTimeout: 12e4,
// httpDownloadInitialTimeoutPerSegment: 17e3,
httpDownloadInitialTimeout: 0,
httpDownloadInitialTimeoutPerSegment: 17e3,
httpUseRanges: !0,
maxBufferLength: 300,
// useP2P: false,
},
};
if (Hls.isSupported() && p2pml.hlsjs.Engine.isSupported()) {
var engine = new p2pml.hlsjs.Engine(engine_config);
player.setup(objSetup);
jwplayer_hls_provider.attach();
p2pml.hlsjs.initJwPlayer(player, {
liveSyncDurationCount: segments_in_queue, // To have at least 7 segments in queue
maxBufferLength: 300,
loader: engine.createLoaderClass(),
});
} else {
player.setup(objSetup);
}
} else {
player.setup(objSetup);
}
const resumeData = 'OPCMS-PlayerPosition-' + id;
player.on('ready', function() {
if (typeof(Storage) !== 'undefined') {
if (localStorage[resumeData] == '' || localStorage[resumeData] == 'undefined') {
console.log("No cookie for position found");
var currentPosition = 0;
} else {
if (localStorage[resumeData] == "null") {
localStorage[resumeData] = 0;
} else {
var currentPosition = localStorage[resumeData];
}
console.log("Position cookie found: " + localStorage[resumeData]);
}
player.once('play', function() {
console.log('Checking position cookie!');
console.log(Math.abs(player.getDuration() - currentPosition));
if (currentPosition > 180 && Math.abs(player.getDuration() - currentPosition) >
5) {
player.seek(currentPosition);
}
});
window.onunload = function() {
localStorage[resumeData] = player.getPosition();
}
} else {
console.log('Your browser is too old!');
}
});
player.on('complete', function() {
if (typeof(Storage) !== 'undefined') {
localStorage.removeItem(resumeData);
} else {
console.log('Your browser is too old!');
}
})
function formatSeconds(seconds) {
var date = new Date(1970, 0, 1);
date.setSeconds(seconds);
return date.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1");
}
}
}
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const episode = '{{ $episode->id }}';
let playing = document.querySelector(`[data-id="${episode}"]`);
if (playing) {
playing.click();
return;
}
const servers = document.getElementsByClassName('streaming-server');
if (servers[0]) {
servers[0].click();
}
});
</script>
{!! setting('site_scripts_facebook_sdk') !!}
@endpush

View File

@ -0,0 +1,17 @@
<div id="widget_top_film_country_by_type_phim-bo" class="topphim-ngang">
<h3>{{ $top['label'] }}</h3>
<ul class="film active">
@foreach ($top['data'] as $movie)
<li>
<a href="{{$movie->getUrl()}}">
<div class="image"
style="background-image:url({{$movie->getPosterUrl()}})">
</div> <span class="imdb">Điểm <br> <b>{{$movie->getRatingStar()}}</b></span>
<div class="info"> <b class="title-film">{{$movie->name}}</b>
<p>{{$movie->origin_name}} ({{$movie->publish_year}})</p>
</div>
</a>
</li>
@endforeach
</ul>
</div>

View File

@ -0,0 +1,17 @@
<div class="top-tintuc">
<h3>{{$top['label']}}</h3>
<ul class="film active">
@foreach ($top['data'] as $movie)
<li>
<a href="{{$movie->getUrl()}}">
<div class="image"
style="background-image:url({{$movie->getPosterUrl()}})">
</div>
<div class="info">
<b class="title-phim">{{$movie->name}} <br /> {{$movie->origin_name}} ({{$movie->publish_year}})</b>
</div>
</a>
</li>
@endforeach
</ul>
</div>

View File

@ -0,0 +1,17 @@
<div class="topphim-doc">
<h3>{{ $top['label'] }}</h3>
<ul class="film">
@foreach ($top['data'] as $movie)
<li> <a href="{{$movie->getUrl()}}">
<div class="image"
style="background-image:url('{{$movie->getThumbUrl()}}')">
</div>
<div class="info"> <b class="title-film">{{$movie->name}}</b>
<p>{{$movie->origin_name}} ({{$movie->publish_year}})</p>
<span class="luotxem">Lượt xem: {{$movie->view_day}}</span>
<span class="imdb">Điểm {{$movie->getRatingStar()}}</span>
</div>
</a> </li>
@endforeach
</ul>
</div>

View File

@ -0,0 +1,50 @@
<ul class="locphim-category">
<form class="form-filter" id="form_filter" method="GET">
<li>
<select name="filter[sort]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
<option value="">-- Sắp xếp --</option>
<option value="update" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'update') selected @endif>Thời gian cập nhật</option>
<option value="create" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'create') selected @endif>Thời gian đăng</option>
<option value="year" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'year') selected @endif>Năm sản xuất</option>
<option value="view" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'view') selected @endif>Lượt xem</option>
</select>
</li>
<li>
<select name="filter[type]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
<option value="">-- Định dạng --</option>
<option value="series" @if (isset(request('filter')['type']) && request('filter')['type'] == 'series') selected @endif>Phim bộ</option>
<option value="single" @if (isset(request('filter')['type']) && request('filter')['type'] == 'single') selected @endif>Phim lẻ</option>
</select>
</li>
<li class="text-center">
<select name="filter[region]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
<option value="">-- Quốc gia --</option>
@foreach (\VsMov\Core\Models\Region::fromCache()->all() as $item)
<option value="{{ $item->id }}" @if ((isset(request('filter')['region']) && request('filter')['region'] == $item->id) ||
(isset($region) && $region->id == $item->id)) selected @endif>
{{ $item->name }}</option>
@endforeach
</select>
</li>
<li>
<select name="filter[category]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
<option value="">-- Thể loại --</option>
@foreach (\VsMov\Core\Models\Category::fromCache()->all() as $item)
<option value="{{ $item->id }}" @if ((isset(request('filter')['category']) && request('filter')['category'] == $item->id) ||
(isset($category) && $category->id == $item->id)) selected @endif>
{{ $item->name }}</option>
@endforeach
</select>
</li>
<li>
<select class="form-control" name="filter[year]" form="form-search">
<option value="">-- Năm phát hành --</option>
@foreach ($years as $year)
<option value="{{ $year }}" @if (isset(request('filter')['year']) && request('filter')['year'] == $year) selected @endif>
{{ $year }}</option>
@endforeach
</select>
</li>
<li> <button type="submit" type="submit" form="form-search" class="submit-filter">Lọc Phim</button> </li>
</form>
</ul>

View File

@ -0,0 +1,23 @@
<a href="{{$movie->getUrl()}}" class="col-xs-4 col-lg-2 film-small">
<div class="poster-film-small"
style="background-image:url('{{$movie->getThumbUrl()}}')">
@if ($movie->type == 'series')
<div class="sotap">{{$movie->episode_current}}</div>
@endif
<ul class="tag-film">
<li>
<div class="hd">{{$movie->quality}}</div>
</li>
@if (strpos(strtolower($movie->language), 'thuyết minh'))
<li>
<div class="sd tm">TM</div>
</li>
@endif
</ul>
<div class="play"></div>
</div>
<div class="title-film-small">
<b class="title-film">{{$movie->name}}</b>
<p>{{$movie->origin_name}} ({{$movie->publish_year}})</p>
</div>
</a>

View File

@ -0,0 +1,64 @@
@php
$logo = setting('site_logo', '');
$brand = setting('site_brand', '');
$title = isset($title) ? $title : setting('site_homepage_title', '');
@endphp
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid navbar-top">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/" title="{{ $title }}">
@if ($logo)
{!! $logo !!}
@else
{!! $brand !!}
@endif
</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
@foreach ($menu as $item)
@if (count($item['children']))
<li class="dropdown">
<a href="{{$item['link']}}" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{$item['name']}} <span
class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
@foreach ($item['children'] as $children)
<li>
<a href="{{$children['link']}}">{{$children['name']}}</a>
</li>
@endforeach
</ul>
</li>
@else
<li>
<a href="{{$item['link']}}">{{$item['name']}}</a>
</li>
@endif
@endforeach
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<form class="navbar-form" method="GET" id="form-search" action="/">
<div class="form-group">
<input placeholder="Tìm tên phim..." class="form-control" id="query_search" value="{{ request('search') }}" type="text" name="search" maxlength="100" autocomplete="off" />
</div>
<button type="submit" class="btn btn-default">
<span class="glyphicon glyphicon-search"></span>
</button>
<div class="search-hint" id="search-hint"></div>
</form>
</li>
</ul>
</div>
</div>
</nav>

View File

@ -0,0 +1,41 @@
@if ($paginator->hasPages())
<ul class="page-category">
@if ($paginator->onFirstPage())
@else
<li>
<a href="{{ $paginator->previousPageUrl() }}" rel="prev"
aria-label="@lang('pagination.previous')">&larr;</a>
</li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="disabled" aria-disabled="true"><a>{{ $element }}</a>
</li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li aria-current="page"><a class="actived">{{ $page }}</a></li>
@else
<li><a href="{{ $url }}">{{ $page }}</a>
</li>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li>
<a href="{{ $paginator->nextPageUrl() }}" rel="next"
aria-label="@lang('pagination.next')">&rarr;</a>
</li>
@else
@endif
</ul>
@endif

View File

@ -0,0 +1,32 @@
<div class="group-film">
<h2>
<a href="">phim đề cử <i class="fa fa-caret-right" aria-hidden="true"></i>
</a>
</h2>
<span class="line-ngang"></span>
<div class="phimdecu-slider">
@foreach ($recommendations as $movie)
<div class="item">
<a href="{{$movie->getUrl()}}"
style="background-image:url({{$movie->getThumbUrl()}})">
<div class="black-gradient">
<b class="title-film">{{$movie->name}}</b>
<p>{{$movie->origin_name}} ({{$movie->publish_year}})</p>
<div class="sotap">{{$movie->episode_current}}</div>
<ul class="tag-film">
<li>
<div class="hd">{{$movie->quality}}</div>
</li>
@if (strpos(strtolower($movie->language), 'thuyết minh'))
<li>
<div class="sd tm">TM</div>
</li>
@endif
</ul>
</div>
<div class="play"></div>
</a>
</div>
@endforeach
</div>
</div>

View File

@ -0,0 +1,65 @@
@extends('themes::themevung.layout')
@php
use VsMov\Core\Models\Movie;
$recommendations = Cache::remember('site.movies.recommendations', setting('site_cache_ttl', 5 * 60), function () {
return Movie::where('is_recommended', true)
->limit(get_theme_option('recommendations_limit', 10))
->orderBy('updated_at', 'desc')
->get();
});
$data = Cache::remember('site.movies.latest', setting('site_cache_ttl', 5 * 60), function () {
$lists = preg_split('/[\n\r]+/', get_theme_option('latest'));
$data = [];
foreach ($lists as $list) {
if (trim($list)) {
$list = explode('|', $list);
[$label, $relation, $field, $val, $sortKey, $alg, $limit, $link] = array_merge($list, ['Phim hot', '', 'type', 'series', 'view_total', 'desc', 12, '']);
try {
$data[] = [
'label' => $label,
'link' => $link,
'data' => \VsMov\Core\Models\Movie::when($relation, function ($query) use ($relation, $field, $val) {
$query->whereHas($relation, function ($rel) use ($field, $val) {
$rel->where($field, $val);
});
})
->when(!$relation, function ($query) use ($field, $val) {
$query->where($field, $val);
})
->orderBy($sortKey, $alg)
->limit($limit)
->get(),
];
} catch (\Exception $e) {
# code
}
}
}
return $data;
});
@endphp
@section('content')
@if (count($recommendations))
@include('themes::themevung.inc.slider_recommended')
@endif
@foreach ($data as $item)
<div class="group-film">
<h2>
<a href="{{$item['link']}}">{{$item['label']}} <i class="fa fa-caret-right" aria-hidden="true"></i>
</a>
</h2>
<a href="{{$item['link']}}" class="more"></a>
<span class="line-ngang"></span>
<div class="row group-film-small">
@foreach ($item['data'] as $movie)
@include('themes::themevung.inc.movie_card')
@endforeach
</div>
</div>
@endforeach
@endsection

View File

@ -0,0 +1,68 @@
@extends('themes::layout')
@php
$menu = \VsMov\Core\Models\Menu::getTree();
$tops = Cache::remember('site.movies.tops', setting('site_cache_ttl', 5 * 60), function () {
$lists = preg_split('/[\n\r]+/', get_theme_option('hotest'));
$data = [];
foreach ($lists as $list) {
if (trim($list)) {
$list = explode('|', $list);
[$label, $relation, $field, $val, $sortKey, $alg, $limit, $template] = array_merge($list, ['Phim hot', '', 'type', 'series', 'view_total', 'desc', 4, 'top_thumb']);
try {
$data[] = [
'label' => $label,
'template' => $template,
'data' => \VsMov\Core\Models\Movie::when($relation, function ($query) use ($relation, $field, $val) {
$query->whereHas($relation, function ($rel) use ($field, $val) {
$rel->where($field, $val);
});
})
->when(!$relation, function ($query) use ($field, $val) {
$query->where($field, $val);
})
->orderBy($sortKey, $alg)
->limit($limit)
->get(),
];
} catch (\Exception $e) {
# code
}
}
}
return $data;
});
@endphp
@push('header')
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:300,300i,400,400i,700,700i" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="{{ asset('/themes/vung/css/v.min.css') }}" as="style" rel="preload" />
@endpush
@section('body')
@include('themes::themevung.inc.nav')
@if (get_theme_option('ads_header'))
{!! get_theme_option('ads_header') !!}
@endif
<div class="container khoi-body">
<div class="khoi-trai">
@yield('content')
</div>
<div class="khoi-phai">
@foreach ($tops as $top)
@include('themes::themevung.inc.aside.' . $top['template'])
@endforeach
</div>
</div>
@endsection
@section('footer')
<script defer type="text/javascript" src="{{ asset('/themes/vung/js/v.min.js') }}"></script>
{!! get_theme_option('footer') !!}
@if (get_theme_option('ads_catfish'))
{!! get_theme_option('ads_catfish') !!}
@endif
{!! setting('site_scripts_google_analytics') !!}
@endsection

View File

@ -0,0 +1,179 @@
@extends('themes::themevung.layout')
@php
$watch_url = '';
if (!$currentMovie->is_copyright && count($currentMovie->episodes) && $currentMovie->episodes[0]['link'] != '') {
$watch_url = $currentMovie->episodes
->sortBy([['server', 'asc']])
->groupBy('server')
->first()
->sortByDesc('name', SORT_NATURAL)
->groupBy('name')
->last()
->sortByDesc('type')
->first()
->getUrl();
}
@endphp
@section('content')
<div class="path-folder-film" style="margin-top: 20px">
<ul itemscope itemtype="http://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
<a itemprop="item" itemprop="url" href="/" title="Xem phim">
<span itemprop="name"> <i class="fa fa-home"></i> Xem phim</span>
</a>
<i class="fa fa-angle-right"></i>
<meta itemprop="position" content="1" />
</li>
<li>
<a href="javascript:;" class="active">{{$currentMovie->name}}</a>
</li>
</ul>
</div>
<div class="group-detail" itemscope itemtype="http://schema.org/Movie">
<div style="display: none">
<div itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">
<span itemprop="ratingValue">{{$currentMovie->getRatingStar()}}</span>
<meta itemprop="bestRating" content="10" />
<meta itemprop="worstRating" content="1" />
<span itemprop="ratingCount">{{$currentMovie->getRatingCount()}}</span>
</div>
</div>
<a href="{{ $watch_url }}" class="big-img-film-detail {{ !$watch_url ? 'comingsoon' : '' }}">
<img src="{{ $currentMovie->getPosterUrl() ?: '/themes/vung/img/big-img-detail.jpg' }}"
style="position: absolute; width: 100%; height: 100%" />
<div style="position: absolute; width: 100%; height: 100%">
<i class="fa fa-play-circle" aria-hidden="true"></i>
</div>
</a>
<h1 class="title-film-detail-1" itemprop="name">{{ $currentMovie->name }} </h1>
<h2 class="title-film-detail-2">{{ $currentMovie->origin_name }} ({{ $currentMovie->publish_year }})</h2>
<div class="fb-gg">
<div class="fb-like" data-href="{{$currentMovie->getUrl()}}"
data-layout="button_count" data-action="like" data-size="small" data-show-faces="false" data-share="true">
</div>
<div class="g-plusone" data-size="medium"></div>
</div>
<div class="imdb">Điểm {{$currentMovie->getRatingStar()}}</div>
<ul class="rated-star hidden-xs">
<i id="star"></i>
</ul>
<span class="rated-text">({{$currentMovie->getRatingCount()}} Đánh giá)</span>
<span class="hd">{{ $currentMovie->quality }}</span>
<br>
@if ($watch_url)
<a href="{{ $watch_url }}" class="play-film">Xem Phim <i class="fa fa-caret-right" aria-hidden="true"></i>
@endif
</a>
@if ($currentMovie->trailer_url && strpos($currentMovie->trailer_url, 'youtube'))
<a href="{{ $currentMovie->trailer_url }}" class="play-trailer">Xem Trailer<i class="fa fa-caret-right"
aria-hidden="true"></i></a>
@endif
@if ($currentMovie->notify && $currentMovie->notify != '')
<p class="lichchieu"><span class="text-danger"> Thông báo</span>: {{ strip_tags($currentMovie->notify) }}</p>
@endif
@if ($currentMovie->showtimes && $currentMovie->showtimes != '')
<p class="lichchieu"><span class="text-info"> Lịch chiếu</span>: {{ strip_tags($currentMovie->showtimes) }}
</p>
@endif
<ul class="infomation-film">
<li class="title">Thông tin:</li>
<li>Trạng thái: <span>{{ $currentMovie->episode_current }}</span>
</li>
<li>Thời lượng: <span>{{ $currentMovie->episode_time }}</span>
</li>
<li>Đạo diễn
{!! $currentMovie->directors->map(function ($director) {
return '<a href="' . $director->getUrl() . '" title="' . $director->name . '">' . $director->name . '</a>';
})->implode(', ') !!}
</li>
<li>Diễn viên:
{!! $currentMovie->actors->map(function ($actor) {
return '<a href="' . $actor->getUrl() . '" title="' . $actor->name . '">' . $actor->name . '</a>';
})->implode(', ') !!}
</li>
<li>Thể loại:
{!! $currentMovie->categories->map(function ($category) {
return '<a href="' . $category->getUrl() . '" title="' . $category->name . '">' . $category->name . '</a>';
})->implode(', ') !!}
</li>
<li>Quốc gia:
{!! $currentMovie->regions->map(function ($region) {
return '<a href="' . $region->getUrl() . '" title="' . $region->name . '">' . $region->name . '</a>';
})->implode(', ') !!}
</li>
<li>Số tập: <span>{{ $currentMovie->episode_total }}</span>
</li>
<li class="tags">
<span>TAGS: </span>
{!! $currentMovie->tags->map(function ($tag) {
return '<a href="' . $tag->getUrl() . '" title="' . $tag->name . '">' . $tag->name . '</a>';
})->implode(', ') !!}
</li>
</ul>
<p class="content-film">
@if ($currentMovie->content)
{!! strip_tags($currentMovie->content) !!}
@else
<p>Hãy xem phim để cảm nhận...</p>
@endif
</p>
</div>
<div class="group-vote-detail">
<h2>Đánh giá phim này</h2>
<ul>
<li class="star" id="star-vote"></li>
</ul>
</div>
<div class="fbchat">
<div class="fb-comments" data-width="100%" data-include-parent="false" data-href="{{ $currentMovie->getUrl() }}"
data-numposts="10" data-order-by="reverse_time" data-colorscheme="dark"></div>
</div>
<div class="group-film">
<h2>phim liên quan <i class="fa fa-caret-right" aria-hidden="true"></i>
</h2>
<span class="line-ngang"></span>
<div class="group-film-small">
@foreach ($movie_related as $movie)
@include('themes::themevung.inc.movie_card')
@endforeach
</div>
</div>
<div class="group-tag-detail">
<h3>
<small>{{ $currentMovie->name }} VietSub, {{ $currentMovie->name }} thuyết minh, {{ $currentMovie->name }}
HD, {{ $currentMovie->name }}, Vượt
Ngục: Phần 1 full/trọn bộ, {{ $currentMovie->name }} phụ đề, {{ $currentMovie->name }} trailer, Vuot Nguc:
Phan 1 VietSub,
Vuot Nguc: Phan 1 thuyet minh, Vuot Nguc: Phan 1 HD, Vuot Nguc: Phan 1, Vuot Nguc: Phan 1 full/tron bo, Vuot
Nguc: Phan 1 phu de, Vuot Nguc: Phan 1 trailer Xem phim {{ $currentMovie->originName }},
{{ $currentMovie->originName }},
{{ $currentMovie->originName }} VietSub, {{ $currentMovie->originName }} Thuyết minh,
{{ $currentMovie->originName }} full HD, Prison
Break: Season 1 bản đẹp, {{ $currentMovie->originName }} trọn bộ, {{ $currentMovie->originName }} phụ đề,
Prison Break: Season
1 trailer</small>
</h3>
</div>
<div class="modal fade" id="mediaModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body modal-padding">
<!-- content dynamically inserted -->
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
var rating = '{{$currentMovie->getRatingStar()}}';
var URL_POST_RATING = '{{ route('movie.rating', ['movie' => $currentMovie->slug]) }}';
</script>
<script defer type="text/javascript" src="/themes/vung/js/rating.js"></script>
<script defer type="text/javascript" src="/themes/vung/js/single.js"></script>
{!! setting('site_scripts_facebook_sdk') !!}
@endpush

55
routes/web.php Normal file
View File

@ -0,0 +1,55 @@
<?php
use Illuminate\Support\Facades\Route;
use VsMov\ThemeVung\Controllers\ThemeVungController;
// --------------------------
// Custom Backpack Routes
// --------------------------
// This route file is loaded automatically by Backpack\Base.
// Routes you generate using Backpack\Generators will be placed here.
Route::group([
'middleware' => array_merge(
(array) config('backpack.base.web_middleware', 'web'),
),
], function () {
Route::get('/', [ThemeVungController::class, 'index']);
Route::get(setting('site_routes_category', '/the-loai/{category}'), [ThemeVungController::class, 'getMovieOfCategory'])
->where(['category' => '.+', 'id' => '[0-9]+'])
->name('categories.movies.index');
Route::get(setting('site_routes_region', '/quoc-gia/{region}'), [ThemeVungController::class, 'getMovieOfRegion'])
->where(['region' => '.+', 'id' => '[0-9]+'])
->name('regions.movies.index');
Route::get(setting('site_routes_tag', '/tu-khoa/{tag}'), [ThemeVungController::class, 'getMovieOfTag'])
->where(['tag' => '.+', 'id' => '[0-9]+'])
->name('tags.movies.index');
Route::get(setting('site_routes_types', '/danh-sach/{type}'), [ThemeVungController::class, 'getMovieOfType'])
->where(['type' => '.+', 'id' => '[0-9]+'])
->name('types.movies.index');
Route::get(setting('site_routes_actors', '/dien-vien/{actor}'), [ThemeVungController::class, 'getMovieOfActor'])
->where(['actor' => '.+', 'id' => '[0-9]+'])
->name('actors.movies.index');
Route::get(setting('site_routes_directors', '/dao-dien/{director}'), [ThemeVungController::class, 'getMovieOfDirector'])
->where(['director' => '.+', 'id' => '[0-9]+'])
->name('directors.movies.index');
Route::get(setting('site_routes_episode', '/phim/{movie}/{episode}-{id}'), [ThemeVungController::class, 'getEpisode'])
->where(['movie' => '.+', 'movie_id' => '[0-9]+', 'episode' => '.+', 'id' => '[0-9]+'])
->name('episodes.show');
Route::post(sprintf('/%s/{movie}/{episode}/report', config('vsmov.routes.movie', 'phim')), [ThemeVungController::class, 'reportEpisode'])->name('episodes.report');
Route::post(sprintf('/%s/{movie}/rate', config('vsmov.routes.movie', 'phim')), [ThemeVungController::class, 'rateMovie'])->name('movie.rating');
Route::get(setting('site_routes_movie', '/phim/{movie}'), [ThemeVungController::class, 'getMovieOverview'])
->where(['movie' => '.+', 'id' => '[0-9]+'])
->name('movies.show');
});

View File

@ -0,0 +1,289 @@
<?php
namespace VsMov\ThemeVung\Controllers;
use Backpack\Settings\app\Models\Setting;
use Illuminate\Http\Request;
use VsMov\Core\Models\Actor;
use VsMov\Core\Models\Catalog;
use VsMov\Core\Models\Category;
use VsMov\Core\Models\Director;
use VsMov\Core\Models\Episode;
use VsMov\Core\Models\Movie;
use VsMov\Core\Models\Region;
use VsMov\Core\Models\Tag;
use Illuminate\Support\Facades\Cache;
class ThemeVungController
{
public function index(Request $request)
{
if ($request['search'] || $request['filter']) {
$data = Movie::when(!empty($request['filter']['category']), function ($movie) {
$movie->whereHas('categories', function ($categories) {
$categories->where('id', request('filter')['category']);
});
})->when(!empty($request['filter']['region']), function ($movie) {
$movie->whereHas('regions', function ($regions) {
$regions->where('id', request('filter')['region']);
});
})->when(!empty($request['filter']['year']), function ($movie) {
$movie->where('publish_year', request('filter')['year']);
})->when(!empty($request['filter']['type']), function ($movie) {
$movie->where('type', request('filter')['type']);
})->when(!empty($request['search']), function ($query) {
$query->where(function ($query) {
$query->where('name', 'like', '%' . request('search') . '%')
->orWhere('origin_name', 'like', '%' . request('search') . '%');
})->orderBy('name', 'desc');
})->when(!empty($request['filter']['sort']), function ($movie) {
if (request('filter')['sort'] == 'create') {
return $movie->orderBy('created_at', 'desc');
}
if (request('filter')['sort'] == 'update') {
return $movie->orderBy('updated_at', 'desc');
}
if (request('filter')['sort'] == 'year') {
return $movie->orderBy('publish_year', 'desc');
}
if (request('filter')['sort'] == 'view') {
return $movie->orderBy('view_total', 'desc');
}
})->paginate(get_theme_option('per_page_limit'));
return view('themes::themevung.catalog', [
'data' => $data,
'search' => $request['search'],
'section_name' => "Tìm kiếm phim: $request->search"
]);
}
return view('themes::themevung.index', [
'title' => Setting::get('site_homepage_title')
]);
}
public function getMovieOverview(Request $request)
{
/** @var Movie */
$movie = Movie::fromCache()->find($request->movie ?: $request->id);
if (is_null($movie)) abort(404);
$movie->generateSeoTags();
$movie->increment('view_total', 1);
$movie->increment('view_day', 1);
$movie->increment('view_week', 1);
$movie->increment('view_month', 1);
$movie_related_cache_key = 'movie_related:' . $movie->id;
$movie_related = Cache::get($movie_related_cache_key);
if(is_null($movie_related)) {
$movie_related = $movie->categories[0]->movies()->inRandomOrder()->limit(get_theme_option('movie_related_limit', 10))->get();
Cache::put($movie_related_cache_key, $movie_related, setting('site_cache_ttl', 5 * 60));
}
return view('themes::themevung.single', [
'currentMovie' => $movie,
'title' => $movie->getTitle(),
'movie_related' => $movie_related
]);
}
public function getEpisode(Request $request)
{
$movie = Movie::fromCache()->find($request->movie ?: $request->movie_id)->load('episodes');
if (is_null($movie)) abort(404);
/** @var Episode */
$episode_id = $request->id;
$episode = $movie->episodes->when($episode_id, function ($collection, $episode_id) {
return $collection->where('id', $episode_id);
})->firstWhere('slug', $request->episode);
if (is_null($episode)) abort(404);
$episode->generateSeoTags();
$movie->increment('view_total', 1);
$movie->increment('view_day', 1);
$movie->increment('view_week', 1);
$movie->increment('view_month', 1);
$movie_related_cache_key = 'movie_related:' . $movie->id;
$movie_related = Cache::get($movie_related_cache_key);
if(is_null($movie_related)) {
$movie_related = $movie->categories[0]->movies()->inRandomOrder()->limit(get_theme_option('movie_related_limit', 10))->get();
Cache::put($movie_related_cache_key, $movie_related, setting('site_cache_ttl', 5 * 60));
}
return view('themes::themevung.episode', [
'currentMovie' => $movie,
'movie_related' => $movie_related,
'episode' => $episode,
'title' => $episode->getTitle()
]);
}
public function getMovieOfCategory(Request $request)
{
/** @var Category */
$category = Category::fromCache()->find($request->category ?: $request->id);
if (is_null($category)) abort(404);
$category->generateSeoTags();
$movies = $category->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
return view('themes::themevung.catalog', [
'data' => $movies,
'category' => $category,
'title' => $category->seo_title ?: $category->getTitle(),
'section_name' => "Phim thể loại $category->name"
]);
}
public function getMovieOfRegion(Request $request)
{
/** @var Region */
$region = Region::fromCache()->find($request->region ?: $request->id);
if (is_null($region)) abort(404);
$region->generateSeoTags();
$movies = $region->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
return view('themes::themevung.catalog', [
'data' => $movies,
'region' => $region,
'title' => $region->seo_title ?: $region->getTitle(),
'section_name' => "Phim quốc gia $region->name"
]);
}
public function getMovieOfActor(Request $request)
{
/** @var Actor */
$actor = Actor::fromCache()->find($request->actor ?: $request->id);
if (is_null($actor)) abort(404);
$actor->generateSeoTags();
$movies = $actor->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
return view('themes::themevung.catalog', [
'data' => $movies,
'person' => $actor,
'title' => $actor->getTitle(),
'section_name' => "Diễn viên $actor->name"
]);
}
public function getMovieOfDirector(Request $request)
{
/** @var Director */
$director = Director::fromCache()->find($request->director ?: $request->id);
if (is_null($director)) abort(404);
$director->generateSeoTags();
$movies = $director->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
return view('themes::themevung.catalog', [
'data' => $movies,
'person' => $director,
'title' => $director->getTitle(),
'section_name' => "Đạo diễn $director->name"
]);
}
public function getMovieOfTag(Request $request)
{
/** @var Tag */
$tag = Tag::fromCache()->find($request->tag ?: $request->id);
if (is_null($tag)) abort(404);
$tag->generateSeoTags();
$movies = $tag->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
return view('themes::themevung.catalog', [
'data' => $movies,
'tag' => $tag,
'title' => $tag->getTitle(),
'section_name' => "Tags: $tag->name"
]);
}
public function getMovieOfType(Request $request)
{
/** @var Catalog */
$catalog = Catalog::fromCache()->find($request->type ?: $request->id);
if (is_null($catalog)) abort(404);
$catalog->generateSeoTags();
$cache_key = 'catalog:' . $catalog->id . ':page:' . ($request['page'] ?: 1);
$movies = Cache::get($cache_key);
if(is_null($movies)) {
$value = explode('|', trim($catalog->value));
[$relation_config, $field, $val, $sortKey, $alg] = array_merge($value, ['', 'is_copyright', 0, 'created_at', 'desc']);
$relation_config = explode(',', $relation_config);
[$relation_table, $relation_field, $relation_val] = array_merge($relation_config, ['', '', '']);
try {
$movies = \VsMov\Core\Models\Movie::when($relation_table, function ($query) use ($relation_table, $relation_field, $relation_val, $field, $val) {
$query->whereHas($relation_table, function ($rel) use ($relation_field, $relation_val, $field, $val) {
$rel->where($relation_field, $relation_val)->where(array_combine(explode(",", $field), explode(",", $val)));
});
})->when(!$relation_table, function ($query) use ($field, $val) {
$query->where(array_combine(explode(",", $field), explode(",", $val)));
})
->orderBy($sortKey, $alg)
->paginate($catalog->paginate);
Cache::put($cache_key, $movies, setting('site_cache_ttl', 5 * 60));
} catch (\Exception $e) {}
}
return view('themes::themevung.catalog', [
'data' => $movies,
'section_name' => "Danh sách $catalog->name"
]);
}
public function reportEpisode(Request $request, $movie, $slug)
{
$movie = Movie::fromCache()->find($movie)->load('episodes');
$episode = $movie->episodes->when(request('id'), function ($collection) {
return $collection->where('id', request('id'));
})->firstWhere('slug', $slug);
$episode->update([
'report_message' => request('message', ''),
'has_report' => true
]);
return response([], 204);
}
public function rateMovie(Request $request, $movie)
{
$movie = Movie::fromCache()->find($movie);
$movie->refresh()->increment('rating_count', 1, [
'rating_star' => $movie->rating_star + ((int) request('rating') - $movie->rating_star) / ($movie->rating_count + 1)
]);
return response()->json(['status' => true, 'rating_star' => number_format($movie->rating_star, 1), 'rating_count' => $movie->rating_count]);
}
}

View File

@ -0,0 +1,178 @@
<?php
namespace VsMov\ThemeVung;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class ThemeVungServiceProvider extends ServiceProvider
{
public function register()
{
$this->setupDefaultThemeCustomizer();
}
public function boot()
{
$this->loadViewsFrom(__DIR__ . '/../resources/views/', 'themes');
$this->publishes([
__DIR__ . '/../resources/assets' => public_path('themes/vung')
], 'vung-assets');
}
protected function setupDefaultThemeCustomizer()
{
config(['themes' => array_merge(config('themes', []), [
'vung' => [
'name' => 'Vung',
'author' => 'vsmov@gmail.com',
'package_name' => 'vsmov/theme-vung',
'publishes' => ['vung-assets'],
'preview_image' => '',
'options' => [
[
'name' => 'recommendations_limit',
'label' => 'Recommended movies limit',
'type' => 'number',
'value' => 10,
'wrapperAttributes' => [
'class' => 'form-group col-md-4',
],
'tab' => 'List'
],
[
'name' => 'per_page_limit',
'label' => 'Pages limit',
'type' => 'number',
'value' => 36,
'wrapperAttributes' => [
'class' => 'form-group col-md-4',
],
'tab' => 'List'
],
[
'name' => 'movie_related_limit',
'label' => 'Movies related limit',
'type' => 'number',
'value' => 12,
'wrapperAttributes' => [
'class' => 'form-group col-md-4',
],
'tab' => 'List'
],
[
'name' => 'latest',
'label' => 'Home Page',
'type' => 'code',
'hint' => 'display_label|relation|find_by_field|value|limit|show_more_url',
'value' => <<<EOT
Phim chiếu rạp||is_shown_in_theater|1|updated_at|desc|12|/danh-sach/phim-chieu-rap
Phim bộ mới||type|series|updated_at|desc|12|/danh-sach/phim-bo
Phim lẻ mới||type|single|updated_at|desc|12|/danh-sach/phim-le
Hoạt hình|categories|slug|hoat-hinh|updated_at|desc|12|/the-loai/hoat-hinh
EOT,
'attributes' => [
'rows' => 5
],
'tab' => 'List'
],
[
'name' => 'hotest',
'label' => 'Danh sách hot',
'type' => 'code',
'hint' => 'Label|relation|find_by_field|value|sort_by_field|sort_algo|limit|show_template (top_thumb|top_poster|top_poster_small)',
'value' => <<<EOT
Phim sắp chiếu||type|single|view_week|desc|10|top_poster_small
Top phim lẻ||type|single|view_week|desc|5|top_thumb
Top phim bộ||type|series|view_week|desc|10|top_poster
EOT,
'attributes' => [
'rows' => 5
],
'tab' => 'List'
],
[
'name' => 'additional_css',
'label' => 'Additional CSS',
'type' => 'code',
'value' => "",
'tab' => 'Custom CSS'
],
[
'name' => 'body_attributes',
'label' => 'Body attributes',
'type' => 'text',
'value' => "class='body-page '",
'tab' => 'Custom CSS'
],
[
'name' => 'additional_header_js',
'label' => 'Header JS',
'type' => 'code',
'value' => "",
'tab' => 'Custom JS'
],
[
'name' => 'additional_body_js',
'label' => 'Body JS',
'type' => 'code',
'value' => "",
'tab' => 'Custom JS'
],
[
'name' => 'additional_footer_js',
'label' => 'Footer JS',
'type' => 'code',
'value' => "",
'tab' => 'Custom JS'
],
[
'name' => 'footer',
'label' => 'Footer',
'type' => 'code',
'value' => <<<EOT
<footer>
<div class="footer1">
<a href="/" style="background-image:url(https://vsmov.com/VSmov.png)"></a>
<ul>
<li>
<a href="#">Hỏi đáp - Hướng dẫn</a>
</li>
<li>
<a href="#">Điều khoản sử dụng</a>
</li>
<li>
<a href="#">Chính sách riêng </a>
</li>
<li>
<a href="#">Nguyên tắc Cộng Đồng</a>
</li>
<li>
<a href="#">Liên hệ Quảng Cáo</a>
</li>
</ul>
<div>Copyright ©2022 VSMOV.</div>
</div>
</footer>
EOT,
'tab' => 'Custom HTML'
],
[
'name' => 'ads_header',
'label' => 'Ads header',
'type' => 'code',
'value' => '',
'tab' => 'Ads'
],
[
'name' => 'ads_catfish',
'label' => 'Ads catfish',
'type' => 'code',
'value' => '',
'tab' => 'Ads'
]
],
]
])]);
}
}

BIN
src/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB