Files
easystream-main/f_templates/tpl_frontend/tpl_index_personalized.tpl
SamiAhmed7777 0b7e2d0a5b feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory
- Added PROJECT_OVERVIEW.md with feature highlights and getting started guide
- Added ARCHITECTURE.md with system design and technical details
- Added SECURITY.md with comprehensive security implementation guide
- Added DEVELOPMENT.md with development workflows and best practices
- Added DEPLOYMENT.md with production deployment instructions
- Added API.md with complete REST API documentation
- Added CONTRIBUTING.md with contribution guidelines
- Added CHANGELOG.md with version history and migration notes
- Reorganized all documentation files into docs/ directory for better organization
- Updated README.md with proper documentation links and quick navigation
- Enhanced project structure with professional documentation standards
2025-10-21 00:39:45 -07:00

384 lines
11 KiB
Smarty

{*
/*******************************************************************************************************************
| Software Name : EasyStream
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
| Software Author : (c) Sami Ahmed
|*******************************************************************************************************************
| This source file is subject to the EasyStream Proprietary License Agreement.
|*******************************************************************************************************************
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|*******************************************************************************************************************/
*}
{* Personalized Homepage Template *}
<div class="personalized-homepage">
{* Hero Section / Featured Content *}
<div class="hero-section">
{generate_html type="home_layout"}
</div>
{* Personalized Content Sections *}
<div class="content-sections">
{foreach from=$personalized_sections key=section_key item=section}
<section class="content-section section-{$section_key}" data-type="{$section.type}">
<div class="section-header">
<h2 class="section-title">{$section.title}</h2>
{if $section_key == 'for_you' && $is_logged_in}
<div class="section-info" title="Personalized based on your watch history, likes, and subscriptions">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM7 4v1h2V4H7zm0 2v6h2V6H7z"/>
</svg>
</div>
{/if}
</div>
<div class="section-content">
{if !empty($section.items)}
<div class="content-grid {if $section.type == 'short'}shorts-grid{else}videos-grid{/if}">
{foreach from=$section.items item=item}
{if $section.type == 'mixed'}
{* Continue Watching Items *}
<div class="content-item continue-watching-item">
<a href="{$main_url}/watch?v={$item.file_key}" class="item-thumbnail">
<img src="{$main_url}/f_data/data_userfiles/{$item.usr_key}/t/{$item.file_key}/0.jpg"
alt="{$item.file_title|escape}"
loading="lazy">
{* Progress Bar *}
<div class="progress-bar">
<div class="progress-fill" style="width: {$item.watch_progress}%"></div>
</div>
{* File Type Badge *}
<span class="type-badge">{$item.file_type|upper}</span>
</a>
<div class="item-info">
<h3 class="item-title">
<a href="{$main_url}/watch?v={$item.file_key}">{$item.file_title|escape|truncate:60}</a>
</h3>
<div class="item-meta">
<a href="{$main_url}/profile?user={$item.usr_user}" class="channel-name">{$item.usr_dname|escape}</a>
<span class="progress-text">{$item.watch_progress}% watched</span>
</div>
</div>
</div>
{else}
{* Regular Video/Short/Live Items *}
<div class="content-item {$section.type}-item">
<a href="{$main_url}/watch?v={$item.file_key}" class="item-thumbnail">
<img src="{$main_url}/f_data/data_userfiles/{$item.usr_key}/t/{$item.file_key}/0.jpg"
alt="{$item.file_title|escape}"
loading="lazy">
{if $item.file_duration}
<span class="duration">{$item.file_duration|date_format:"%M:%S"}</span>
{/if}
{if $section.type == 'live' && $item.stream_live == 1}
<span class="live-badge">LIVE</span>
{/if}
</a>
<div class="item-info">
<h3 class="item-title">
<a href="{$main_url}/watch?v={$item.file_key}">{$item.file_title|escape|truncate:60}</a>
</h3>
<div class="item-meta">
<a href="{$main_url}/profile?user={$item.usr_user}" class="channel-name">{$item.usr_dname|escape}</a>
<div class="stats">
<span class="views">{$item.file_views|number_format} views</span>
{if $item.upload_date}
<span class="separator">•</span>
<span class="date">{$item.upload_date|date_format:"%b %d, %Y"}</span>
{/if}
</div>
</div>
</div>
</div>
{/if}
{/foreach}
</div>
{else}
<div class="empty-section">
<p>No content available in this section yet.</p>
</div>
{/if}
</div>
</section>
{/foreach}
</div>
</div>
<style>
/* Personalized Homepage Styles */
.personalized-homepage {
max-width: 1920px;
margin: 0 auto;
padding: 20px;
}
.content-sections {
margin-top: 30px;
}
.content-section {
margin-bottom: 50px;
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e0e0e0;
}
.section-title {
font-size: 24px;
font-weight: 600;
color: #030303;
margin: 0;
flex: 1;
}
.section-info {
color: #606060;
cursor: help;
margin-left: 8px;
}
/* Video Grid Layout */
.videos-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
/* Shorts Grid Layout */
.shorts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.content-item {
display: flex;
flex-direction: column;
cursor: pointer;
}
.item-thumbnail {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
background: #f0f0f0;
border-radius: 12px;
overflow: hidden;
display: block;
}
.shorts-grid .item-thumbnail {
padding-bottom: 177.78%; /* 9:16 aspect ratio for shorts */
}
.item-thumbnail img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.2s;
}
.content-item:hover .item-thumbnail img {
transform: scale(1.05);
}
.duration,
.live-badge,
.type-badge {
position: absolute;
bottom: 8px;
right: 8px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.live-badge {
background: #cc0000;
}
.type-badge {
top: 8px;
right: 8px;
bottom: auto;
background: rgba(0, 0, 0, 0.7);
font-size: 10px;
padding: 2px 4px;
}
/* Progress Bar for Continue Watching */
.progress-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
}
.progress-fill {
height: 100%;
background: #cc0000;
transition: width 0.3s;
}
.item-info {
padding: 12px 0;
}
.item-title {
margin: 0 0 8px 0;
font-size: 14px;
font-weight: 500;
line-height: 1.4;
}
.item-title a {
color: #030303;
text-decoration: none;
}
.item-title a:hover {
color: #cc0000;
}
.item-meta {
font-size: 13px;
color: #606060;
}
.channel-name {
color: #606060;
text-decoration: none;
display: block;
margin-bottom: 4px;
}
.channel-name:hover {
color: #030303;
}
.stats {
display: flex;
align-items: center;
gap: 6px;
}
.separator {
font-size: 10px;
}
.progress-text {
color: #065fd4;
font-weight: 500;
}
.empty-section {
padding: 40px;
text-align: center;
color: #606060;
}
/* Responsive Design */
@media (max-width: 1024px) {
.videos-grid {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
}
@media (max-width: 768px) {
.videos-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.shorts-grid {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}
.section-title {
font-size: 20px;
}
}
@media (max-width: 480px) {
.videos-grid,
.shorts-grid {
grid-template-columns: 1fr;
}
.personalized-homepage {
padding: 12px;
}
}
/* Dark Mode Support */
@media (prefers-color-scheme: dark) {
.section-header {
border-bottom-color: #303030;
}
.section-title {
color: #f1f1f1;
}
.item-title a {
color: #f1f1f1;
}
.item-thumbnail {
background: #1f1f1f;
}
.empty-section {
color: #aaa;
}
}
</style>
<script>
// Lazy loading for images
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src || img.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('.item-thumbnail img').forEach(img => {
imageObserver.observe(img);
});
}
// Track video clicks for improving recommendations
document.querySelectorAll('.content-item a').forEach(link => {
link.addEventListener('click', function(e) {
const fileKey = this.href.match(/v=([^&]+)/);
if (fileKey && fileKey[1]) {
// Optional: Send analytics event
// fetch('/api/track_click.php?v=' + fileKey[1]);
}
});
});
</script>