Initial commit

This commit is contained in:
vsphim
2026-05-20 12:51:54 +07:00
commit 7e9bc5c8e0
57 changed files with 4180 additions and 0 deletions

View File

@@ -0,0 +1,290 @@
<?php
namespace VsMov\ThemeXingKong\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 ThemeXingKongController
{
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->whereRaw("MATCH(name, origin_name) AGAINST(? IN BOOLEAN MODE)", [request('search')]);
});
})->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::themexingkong.catalog', [
'data' => $data,
'search' => $request['search'],
'section_name' => "Tìm kiếm phim: $request->search"
]);
}
return view('themes::themexingkong.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::themexingkong.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::themexingkong.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::themexingkong.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::themexingkong.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::themexingkong.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::themexingkong.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::themexingkong.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::themexingkong.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([], 204);
}
}

View File

@@ -0,0 +1,229 @@
<?php
namespace VsMov\ThemeXingKong;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Cache;
class ThemeXingKongServiceProvider extends ServiceProvider
{
public function register()
{
$this->setupDefaultThemeCustomizer();
}
public function boot()
{
$this->loadViewsFrom(__DIR__ . '/../resources/views/', 'themes');
$this->publishes([
__DIR__ . '/../resources/assets' => public_path('themes/xingkong')
], 'xingkong-assets');
view()->composer('themes::themexingkong.*', function ($view) {
$view->with('menu', \VsMov\Core\Models\Menu::getTree());
});
view()->composer(['themes::themexingkong.inc.right_bar'], function ($view) {
$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] = array_merge($list, ['Phim hot', '', 'type', 'series', 'view_total', 'desc', 4]);
try {
$data[] = [
'label' => $label,
'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;
});
$view->with('tops', $tops);
});
}
protected function setupDefaultThemeCustomizer()
{
config(['themes' => array_merge(config('themes', []), [
'xingkong' => [
'name' => 'Theme XingKong',
'author' => 'vsmov@gmail.com',
'package_name' => 'vsmov/theme-xingkong',
'publishes' => ['xingkong-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' => 30,
'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|display_description|relation|find_by_field|value|sort_by_field|sort_algo|limit|show_more_url|section_template(default|with_top)',
'value' => <<<EOT
Phim chiếu rạp mới||is_shown_in_theater|1|created_at|desc|12|/danh-sach/phim-chieu-rap|default
Phim bộ mới||type|series|updated_at|desc|10|/danh-sach/phim-bo|with_top
Phim lẻ mới||type|single|updated_at|desc|10|/danh-sach/phim-le|with_top
Phim hoạt hình mới|categories|slug|hoat-hinh|updated_at|desc|10|/the-loai/hoat-hinh|with_top
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',
'value' => <<<EOT
Phim sắp chiếu||status|trailer|publish_year|desc|8
Top phim lẻ||type|single|view_week|desc|8
Top phim bộ||type|series|view_week|desc|8
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' => '',
'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="container">
<div class="row">
<div class="stui-foot clearfix">
<p class="text-center hidden-xs">
<a class="fed-font-xiv" href="#" target="_blank">Textlink</a>
<span class="fed-font-xiv"> - </span>
<a class="fed-font-xiv" href="#" target="_blank">Textlink</a>
<span class="fed-font-xiv fed-hide-xs"> - </span>
<a class="fed-font-xiv fed-hide-xs" href="#" target="_blank">Textlink</a>
<span class="fed-font-xiv fed-hide-xs"> - </span>
<a class="fed-font-xiv fed-hide-xs" href="#" target="_blank">Textlink</a>
<span class="fed-font-xiv fed-hide-xs"> - </span>
<a class="fed-font-xiv fed-hide-xs" href="#" target="_blank">Textlink</a>
<span class="fed-font-xiv fed-hide-xs"> - </span>
<a class="fed-font-xiv fed-hide-xs" href="#" target="_blank">Textlink</a>
<span class="fed-font-xiv fed-hide-xs"> - </span>
<a class="fed-font-xiv fed-hide-xs" href="#" target="_blank">Textlink</a>
</p>
</div>
<div class="stui-foot clearfix">
<div class="col-pd text-center hidden-xs">
Dữ liệu phim miễn phí vĩnh viễn. Cập nhật nhanh, chất lượng cao, ổn định và lâu dài. Tốc độ phát cực nhanh với đường truyền băng thông cao, đảm bảo đáp ứng được lượng xem phim trực tuyến lớn. Đồng thời giúp nhà phát triển website phim giảm thiểu chi phí của các dịch vụ lưu trữ và stream.
</div>
<p class="share bdsharebuttonbox text-center margin-0 hidden-sm hidden-xs"></p>
<p class="text-muted text-center visible-xs">Copyright © 2015-2024 <a href="/" target="_blank">VsMov</a>
</p>
</div>
</div>
</div>
</footer>
EOT,
'tab' => 'Custom HTML'
],
[
'name' => 'ads_header',
'label' => 'Ads header',
'type' => 'code',
'value' => <<<EOT
EOT,
'tab' => 'Ads'
],
[
'name' => 'ads_catfish',
'label' => 'Ads catfish',
'type' => 'code',
'value' => <<<EOT
EOT,
'tab' => 'Ads'
]
],
]
])]);
}
}

BIN
src/screenshot.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB