Browse Source

favs manage done

master
filesite 7 months ago
parent
commit
c55814e55d
  1. 3
      plugins/Common.php
  2. 25
      plugins/Html.php
  3. 247
      themes/tajian/controller/FrontapiController.php
  4. 15
      themes/tajian/controller/MyController.php
  5. 2
      themes/tajian/controller/SiteController.php
  6. 83
      themes/tajian/views/my/favs.php
  7. 2
      themes/tajian/views/my/index.php
  8. 8
      www/css/tajian.css
  9. 4
      www/img/trash.svg
  10. 64
      www/js/tajian.js

3
plugins/Common.php

@ -14,6 +14,9 @@ Class Common { @@ -14,6 +14,9 @@ Class Common {
' ',
';',
';',
'.',
'%',
':',
);
return str_replace($findChars, '', $str);

25
plugins/Html.php

@ -94,4 +94,29 @@ eof; @@ -94,4 +94,29 @@ eof;
return $gacode;
}
//根据收藏和分类,获取单个收藏视频的所在分类
public static function getFavsTags($filename, $tags) {
$fileTags = array();
foreach($tags as $tag_id => $item) {
if (in_array($filename, $item['files'])) {
array_push($fileTags, $item['name']);
}
}
return $fileTags;
}
//获取只包含分类名的数组
public static function getTagNames($tags) {
$tmp_arr = array();
foreach ($tags as $id => $tag) {
array_push($tmp_arr, $tag['name']);
}
return $tmp_arr;
}
}

247
themes/tajian/controller/FrontapiController.php

@ -158,7 +158,8 @@ Class FrontApiController extends SiteController { @@ -158,7 +158,8 @@ Class FrontApiController extends SiteController {
$done = $done && $this->saveBotTask($shareUrl);
if (!empty($tagName)) {
$done = $done && $this->saveVideoToTag($shareUrl, $tagName);
$video_id = $this->getVideoId($shareUrl);
$done = $done && $this->saveVideoToTag($video_id, $tagName);
}
//保存任务日志
@ -189,13 +190,14 @@ Class FrontApiController extends SiteController { @@ -189,13 +190,14 @@ Class FrontApiController extends SiteController {
//保存分享视频到tag分类
//TODO: 如果高并发,需要避免数据被覆盖的问题
protected function saveVideoToTag($url, $tagName) {
protected function saveVideoToTag($video_id, $tagName) {
if (empty($video_id) || empty($tagName)) {return false;}
$tag_dir = __DIR__ . '/../../../www/' . FSC::$app['config']['content_directory'] . FSC::$app['config']['tajian']['tag_dir'];
if (!is_dir($tag_dir)) {
mkdir($tag_dir, 0755, true);
}
$video_id = $this->getVideoId($url);
$filepath = realpath($tag_dir) . "/{$tagName}.txt";
if (file_exists($filepath)) {
$content = file_get_contents($filepath);
@ -215,6 +217,35 @@ Class FrontApiController extends SiteController { @@ -215,6 +217,35 @@ Class FrontApiController extends SiteController {
}
}
//从分类中删除视频
protected function deleteVideoFromTag($filename, $tagName) {
if (empty($filename) || empty($tagName)) {return false;}
$tag_dir = __DIR__ . '/../../../www/' . FSC::$app['config']['content_directory'] . FSC::$app['config']['tajian']['tag_dir'];
if (!is_dir($tag_dir)) {
mkdir($tag_dir, 0755, true);
}
$filepath = realpath($tag_dir) . "/{$tagName}.txt";
if (file_exists($filepath)) {
$content = file_get_contents($filepath);
$videos = explode("\n", $content);
$last_id = array_pop($videos);
if (!empty($last_id)) {
array_push($videos, $last_id);
}
$key = array_search($filename, $videos);
if ($key !== false) {
unset($videos[$key]);
}
return file_put_contents($filepath, implode("\n", $videos)) !== false;
}
return false;
}
//保存任务日志
protected function saveTaskLog($url, $title, $tagName) {
$logFile = __DIR__ . '/../../../runtime/' . FSC::$app['config']['tajian']['task_log'];
@ -327,6 +358,51 @@ eof; @@ -327,6 +358,51 @@ eof;
return $done;
}
//删除收藏的视频相关的所有文件
protected function deleteVideoFiles($filename) {
$data_dir = __DIR__ . '/../../../www/' . FSC::$app['config']['content_directory'] . FSC::$app['config']['tajian']['data_dir'];
if (!is_dir($data_dir)) {
return false;
}
$done = true;
try {
$data_dir = realpath($data_dir);
//删除.url文件
$filepath_url = "{$data_dir}/{$filename}.url";
if (file_exists($filepath_url)) {
unlink($filepath_url);
}
//删除标题
$filepath_title = "{$data_dir}/{$filename}_title.txt";
if (file_exists($filepath_title)) {
unlink($filepath_title);
}
//删除图片文件
$imgTypes = array('jpg', 'jpeg', 'png', 'gif', 'webp');
foreach ($imgTypes as $cover_type) {
$filepath_cover = "{$data_dir}/{$filename}.{$cover_type}";
if (file_exists($filepath_cover)) {
unlink($filepath_cover);
}
}
//删除图片描述文件
$filepath_desc = "{$data_dir}/{$filename}_cover.txt";
if (file_exists($filepath_desc)) {
unlink($filepath_desc);
}
}catch(Exception $err) {
$done = false;
}
return $done;
}
//HeroUnion任务数据通知回传接口
/**
* task_id
@ -785,13 +861,10 @@ eof; @@ -785,13 +861,10 @@ eof;
$tags_current = $this->sortTags($menus_sorted, $tags_current);
}
//获取只包含分类名的数组
$tmp_arr = array();
foreach ($tags_current as $id => $tag) {
array_push($tmp_arr, $tag['name']);
}
$allTags = Html::getTagNames($tags_current);
//保存
$saved = $this->saveTags($tags, $tmp_arr);
$saved = $this->saveTags($tags, $allTags);
if (!empty($saved)) {
$msg = "分类已保存";
$code = 1;
@ -840,6 +913,8 @@ eof; @@ -840,6 +913,8 @@ eof;
if (empty($tag_to_delete)) {
$err = "参数错误,缺少tag传参";
}else {
$tag_to_delete = Common::cleanSpecialChars($tag_to_delete);
}
if (empty($err)) { //如果数据检查通过,尝试保存
@ -907,28 +982,14 @@ eof; @@ -907,28 +982,14 @@ eof;
$scanner->setWebRoot(FSC::$app['config']['content_directory']);
$dirTree = $scanner->scan(__DIR__ . '/../../../www/' . FSC::$app['config']['content_directory'], 3);
$menus_sorted = array(); //Readme_sort.txt 说明文件内容,一级目录菜单从上到下的排序
$readmeFile = $scanner->getDefaultReadme();
if (!empty($readmeFile)) {
if (!empty($readmeFile['sort'])) {
$menus_sorted = explode("\n", $readmeFile['sort']);
}
}
//获取tags分类
$tags_current = $this->getTags($dirTree);
//排序
if (!empty($menus_sorted) && !empty($tags_current)) {
$tags_current = $this->sortTags($menus_sorted, $tags_current);
}
//获取只包含分类名的数组
$tmp_arr = array();
foreach ($tags_current as $id => $tag) {
array_push($tmp_arr, $tag['name']);
}
$allTags = Html::getTagNames($tags_current);
//最多添加 20 个分类
if (count($tmp_arr) >= 20) {
if (count($allTags) >= 20) {
$err = '最多添加 20 个分类,请合理规划视频分类哦';
}else {
//保存
@ -946,7 +1007,141 @@ eof; @@ -946,7 +1007,141 @@ eof;
return $this->renderJson(compact('code', 'msg', 'err'));
}
//TODO: 视频管理
//视频管理:把视频从分类中删除、添加视频到某个分类、删除视频
public function actionDeletefav() {
$ip = $this->getUserIp();
$check_time = 120; //2 分钟内
$max_time_in_minutes = 60; //最多 60 次
$isUserGotRequestLimit = $this->requestLimit($ip, $max_time_in_minutes, $check_time);
if ($isUserGotRequestLimit) {
$this->logError("Request limit got, ip: {$ip}");
throw new Exception('Oops,操作太快了,请喝杯咖啡休息会吧...');
}
//只允许修改自己的数据
$loginedUser = Common::getUserFromSession();
if (empty($loginedUser['username'])) {
throw new Exception('Oops,你还没登录哦');
}else if (
!empty(FSC::$app['config']['multipleUserUriParse'])
&& (empty(FSC::$app['user_id']) || FSC::$app['user_id'] != $loginedUser['username'])
) {
throw new Exception('Oops,请求地址有误');
}
//返回给视图的变量
$code = 0;
$msg = '';
$err = '';
//用户提交的数据检查
$postParams = $this->post();
if (!empty($postParams)) {
$video_id = $this->post('id', '');
$video_filename = $this->post('filename', '');
if (empty($video_id) || empty($video_filename)) {
$err = "参数错误,缺少id和filename传参";
}else {
$video_id = Common::cleanSpecialChars($video_id);
$video_filename = Common::cleanSpecialChars($video_filename);
}
if (empty($err)) { //如果数据检查通过,尝试保存
//获取已有的分类
$scanner = new DirScanner();
$scanner->setWebRoot(FSC::$app['config']['content_directory']);
$dirTree = $scanner->scan(__DIR__ . '/../../../www/' . FSC::$app['config']['content_directory'], 3);
//获取tags分类
$tags_current = $this->getTags($dirTree);
//获取当前视频的所属分类数组
$myTags = Html::getFavsTags($video_filename, $tags_current);
foreach ($myTags as $item) {
$this->deleteVideoFromTag($video_filename, $item); //从分类中删除此视频
}
//删除此视频的所有文件
$saved = $this->deleteVideoFiles($video_filename);
if (!empty($saved)) {
$msg = "视频已删除";
$code = 1;
}else {
$err = '视频删除失败,请稍后重试';
}
}
}
return $this->renderJson(compact('code', 'msg', 'err'));
}
public function actionUpdatefavstag() {
$ip = $this->getUserIp();
$check_time = 120; //2 分钟内
$max_time_in_minutes = 60; //最多 60 次
$isUserGotRequestLimit = $this->requestLimit($ip, $max_time_in_minutes, $check_time);
if ($isUserGotRequestLimit) {
$this->logError("Request limit got, ip: {$ip}");
throw new Exception('Oops,操作太快了,请喝杯咖啡休息会吧...');
}
//只允许修改自己的数据
$loginedUser = Common::getUserFromSession();
if (empty($loginedUser['username'])) {
throw new Exception('Oops,你还没登录哦');
}else if (
!empty(FSC::$app['config']['multipleUserUriParse'])
&& (empty(FSC::$app['user_id']) || FSC::$app['user_id'] != $loginedUser['username'])
) {
throw new Exception('Oops,请求地址有误');
}
//返回给视图的变量
$code = 0;
$msg = '';
$err = '';
//用户提交的数据检查
$postParams = $this->post();
if (!empty($postParams)) {
$video_id = $this->post('id', '');
$video_filename = $this->post('filename', '');
$tagName = $this->post('tag', '');
$action = $this->post('do', 'remove'); //添加:add,移除:remove
if (empty($video_id) || empty($video_filename) || empty($tagName)) {
$err = "参数错误,缺少id、filename、tag传参";
}else {
$video_id = Common::cleanSpecialChars($video_id);
$video_filename = Common::cleanSpecialChars($video_filename);
$tagName = Common::cleanSpecialChars($tagName);
}
if (empty($err)) { //如果数据检查通过,尝试保存
$saved = false;
if ($action == 'add') {
$saved = $this->saveVideoToTag($video_filename, $tagName); //添加视频到分类
}else {
$saved = $this->deleteVideoFromTag($video_filename, $tagName); //从分类中删除此视频
}
if ($saved !== false) {
$msg = "操作完成";
$code = 1;
}else {
$err = '操作失败,请稍后重试';
}
}
}
return $this->renderJson(compact('code', 'msg', 'err'));
}
}

15
themes/tajian/controller/MyController.php

@ -28,6 +28,9 @@ Class MyController extends SiteController { @@ -28,6 +28,9 @@ Class MyController extends SiteController {
$dirTree = $scanner->scan(__DIR__ . '/../../../www/' . FSC::$app['config']['content_directory'], 3);
$scanResults = $scanner->getScanResults();
//获取目录
$menus = $scanner->getMenus();
$readmeFile = $scanner->getDefaultReadme();
if (!empty($readmeFile)) {
if (!empty($readmeFile['sort'])) {
@ -40,6 +43,9 @@ Class MyController extends SiteController { @@ -40,6 +43,9 @@ Class MyController extends SiteController {
$htmlReadme = $scanner->fixMDUrls($readmeFile['realpath'], $htmlReadme);
}
//默认显示的目录
$cateId = $menus[0]['id'];
//获取tags分类
$tags = $this->getTags($dirTree);
@ -53,7 +59,7 @@ Class MyController extends SiteController { @@ -53,7 +59,7 @@ Class MyController extends SiteController {
$pageTitle = "{$defaultTitle} | " . FSC::$app['config']['site_name'];
$params = compact(
'dirTree', 'scanResults',
'cateId', 'dirTree', 'scanResults',
'htmlReadme', 'tags', 'nickname'
);
return $this->render($viewName, $params, $pageTitle);
@ -80,4 +86,11 @@ Class MyController extends SiteController { @@ -80,4 +86,11 @@ Class MyController extends SiteController {
return $this->actionIndex($viewName, $defaultTitle);
}
//管理收藏
public function actionFavs() {
$defaultTitle = "管理收藏";
$viewName = 'favs';
return $this->actionIndex($viewName, $defaultTitle);
}
}

2
themes/tajian/controller/SiteController.php

@ -220,6 +220,8 @@ Class SiteController extends Controller { @@ -220,6 +220,8 @@ Class SiteController extends Controller {
//删除分类
protected function deleteTag($tag) {
if (empty($tag)) {return false;}
$done = false;
try {

83
themes/tajian/views/my/favs.php

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
<?php
//常用方法
require_once __DIR__ . '/../../../../plugins/Html.php';
$imgPreffix = '/' . FSC::$app['config']['content_directory'] . FSC::$app['config']['tajian']['data_dir'];
$linkPrefix = '';
//多用户路径支持
if (!empty(FSC::$app['config']['multipleUserUriParse']) && !empty(FSC::$app['user_id'])) {
$linkPrefix = '/' . FSC::$app['user_id'];
}
$selectedId = !empty($viewData['cateId']) ? $viewData['cateId'] : '';
?>
<main class="g_main_lay">
<p>勾选视频下方的分类,将该视频归类到对应的分类;取消勾选,则将视频从该分类中移除。</p>
<div class="videos_list clearfix" id="favmg">
<?php
$imgExts = array('jpg', 'jpeg', 'png', 'gif');
$videoExts = array('url');
$category = $viewData['scanResults'][$selectedId];
$allTags = Html::getTagNames($viewData['tags']);
if (!empty($category['files'])) { //一级目录支持,目录下直接存放视频文件
$cate_files = Html::sortFilesByCreateTime($category['files'], 'desc'); //按创建时间排序
foreach($cate_files as $index => $file) {
//跳过非.url文件
if (!in_array($file['extension'], $videoExts) || empty($file['shortcut'])) {
continue;
}
$snapshot = !empty($file['cover']) ? $imgPreffix . $file['cover'] : '/img/default.png';
$title = !empty($file['title']) ? Html::mb_substr($file['title'], 0, 33, 'utf-8') : $file['filename'];
$platform = Html::getShareVideosPlatform($file['shortcut']['url']);
$pubDate = date('m/d', $file['fstat']['ctime']);
$imgSrc = $index < 4 ? " src=\"{$snapshot}\"" : '';
$imgAlt = $index < 4 ? " alt=\"{$title}\"" : '';
$imgCls = $index < 4 ? '' : 'lazy';
$myTags = Html::getFavsTags($file['filename'], $viewData['tags']);
$tagsHtml = '';
foreach ($allTags as $tagName) {
$tagChecked = in_array($tagName, $myTags) ? ' checked="checked"' : '';
$tagsHtml .= <<<eof
<label data-filename="{$file['filename']}" data-video-id="{$file['id']}" data-tag="{$tagName}"><input type="checkbox" value="{$tagName}"{$tagChecked}> {$tagName}</label>
eof;
}
echo <<<eof
<div class="vl_list_main advideo-item favmg-item">
<div class="video_img_vl">
<a href="{$file['shortcut']['url']}" target="_blank">
<img data-original="{$snapshot}" class="{$imgCls}"{$imgSrc}{$imgAlt}>
</a>
<button type="button" class="btn-danger btn-del" data-video-id="{$file['id']}" data-filename="{$file['filename']}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3" viewBox="0 0 16 16">
<path d="M6.5 1h3a.5.5 0 0 1 .5.5v1H6v-1a.5.5 0 0 1 .5-.5M11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3A1.5 1.5 0 0 0 5 1.5v1H1.5a.5.5 0 0 0 0 1h.538l.853 10.66A2 2 0 0 0 4.885 16h6.23a2 2 0 0 0 1.994-1.84l.853-10.66h.538a.5.5 0 0 0 0-1zm1.958 1-.846 10.58a1 1 0 0 1-.997.92h-6.23a1 1 0 0 1-.997-.92L3.042 3.5zm-7.487 1a.5.5 0 0 1 .528.47l.5 8.5a.5.5 0 0 1-.998.06L5 5.03a.5.5 0 0 1 .47-.53Zm5.058 0a.5.5 0 0 1 .47.53l-.5 8.5a.5.5 0 1 1-.998-.06l.5-8.5a.5.5 0 0 1 .528-.47M8 4.5a.5.5 0 0 1 .5.5v8.5a.5.5 0 0 1-1 0V5a.5.5 0 0 1 .5-.5"></path>
</svg>
删除
</button>
</div>
<div class="video_title_vl">
<a href="{$file['shortcut']['url']}" target="_blank">
<span class="duration">{$platform}</span>
<strong>{$pubDate},{$title}</strong>
</a>
</div>
<div class="act_tags">
{$tagsHtml}
</div>
</div>
eof;
}
}
?>
</div>
</main>

2
themes/tajian/views/my/index.php

@ -21,7 +21,7 @@ if (!empty(FSC::$app['config']['multipleUserUriParse']) && !empty(FSC::$app['use @@ -21,7 +21,7 @@ if (!empty(FSC::$app['config']['multipleUserUriParse']) && !empty(FSC::$app['use
<hr>
<ul class="mg_menus">
<li><a href="<?=$linkPrefix?>/my/tags"><img src="/img/collection.svg" alt="collection" width="18"> 管理分类</a></li>
<li><a href="###"><img src="/img/favorite.png" alt="favorite" width="20"> 管理收藏</a></li>
<li><a href="<?=$linkPrefix?>/my/favs"><img src="/img/favorite.png" alt="favorite" width="20"> 管理收藏</a></li>
<li><a href="<?=$linkPrefix?>/my/addtag"><img src="/img/edit.svg" alt="edit" width="18"> 添加分类</a></li>
<li><a href="<?=$linkPrefix?>/site/new"><img src="/img/addvideos.svg" alt="add favorite" width="20"> 添加收藏</a></li>
</ul>

8
www/css/tajian.css

@ -249,6 +249,8 @@ a:link{text-decoration:none;} @@ -249,6 +249,8 @@ a:link{text-decoration:none;}
.tajian_index .kfwx{float:right;border-radius:6px}
.btn-primary,.btn-danger,.tajian_index .loginbtn{border:1px solid #50509d;background-color:#50509d;color:#FFF;padding:3px 10px;border-radius:4px;cursor:pointer}
.btn-danger{background-color:#d32d2d;border-color:#e96b5b}
.btn-primary:hover,.btn-danger:hover{font-weight:500}
.bi{width:1em;height:1em;display:inline-block;vertical-align:-.125em;fill:currentcolor}
/* 注册/登录 */
.twocol label{display:block}
@ -265,6 +267,12 @@ a:link{text-decoration:none;} @@ -265,6 +267,12 @@ a:link{text-decoration:none;}
.tag-item .btn-danger{float:right}
.tag-item img{cursor:pointer}
/* 收藏管理 */
.favmg-item .btn-del{position:absolute;right:5px;bottom:5px}
.favmg-item .act_tags{min-height:40px;background-color:#EEE;padding:4px;margin-right:4px;margin-top:5px}
.favmg-item .act_tags label{display:inline-block;cursor:pointer}
.favmg-item .act_tags label input{cursor:pointer}
/* layout index */
body.layout_index{background-color:#e5f1f3}
.layout_index .top_nav{padding-left:20%;padding-right:20%}

4
www/img/trash.svg

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3" viewBox="0 0 16 16">
<path d="M6.5 1h3a.5.5 0 0 1 .5.5v1H6v-1a.5.5 0 0 1 .5-.5M11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3A1.5 1.5 0 0 0 5 1.5v1H1.5a.5.5 0 0 0 0 1h.538l.853 10.66A2 2 0 0 0 4.885 16h6.23a2 2 0 0 0 1.994-1.84l.853-10.66h.538a.5.5 0 0 0 0-1zm1.958 1-.846 10.58a1 1 0 0 1-.997.92h-6.23a1 1 0 0 1-.997-.92L3.042 3.5zm-7.487 1a.5.5 0 0 1 .528.47l.5 8.5a.5.5 0 0 1-.998.06L5 5.03a.5.5 0 0 1 .47-.53Zm5.058 0a.5.5 0 0 1 .47.53l-.5 8.5a.5.5 0 1 1-.998-.06l.5-8.5a.5.5 0 0 1 .528-.47M8 4.5a.5.5 0 0 1 .5.5v8.5a.5.5 0 0 1-1 0V5a.5.5 0 0 1 .5-.5"/>
</svg>

After

Width:  |  Height:  |  Size: 696 B

64
www/js/tajian.js

@ -11,6 +11,8 @@ var taJian = { @@ -11,6 +11,8 @@ var taJian = {
saveTags: '/frontapi/savetags', //保存分类
deleteTag: '/frontapi/deletetag', //删除分类
addTag: '/frontapi/addtag', //添加分类
updateFavsTag: '/frontapi/updatefavstag', //修改视频的分类
deleteFav: '/frontapi/deletefav', //删除收藏的视频
sendSmsCode: '/frontapi/sendsmscode', //发送短信验证码
register: '/frontapi/createuser', //注册
login: '/frontapi/loginuser' //登入
@ -538,4 +540,66 @@ if ($('#tag_new_form').get(0)) { @@ -538,4 +540,66 @@ if ($('#tag_new_form').get(0)) {
});
}
// 视频管理
if ($('#favmg').get(0)) {
$('#favmg .act_tags input[type=checkbox]').change(function(e) {
var checkbox = e.target;
var action = checkbox.checked ? 'add' : 'remove';
var label = $(checkbox).parents('label'),
video_id = label.attr('data-video-id'),
filename = label.attr('data-filename'),
tag = label.attr('data-tag');
$(checkbox).prop('disabled', true);
var datas = {
'do': action,
'id': video_id,
'filename': filename,
'tag': tag
};
publicAjax(taJian.apis.updateFavsTag, 'POST', datas, function (data) {
$(checkbox).prop('disabled', false);
if (data.code != 1) {
$(checkbox).prop('checked', !checkbox.checked);
alert(data.err);
}
}, function (jqXHR, textStatus, errorThrown) {
$(checkbox).prop('disabled', false);
alert('网络请求失败,请重试。');
});
});
$('#favmg .favmg-item .btn-del').click(function(e) {
var target = e.target;
if (target.tagName.toLowerCase() != 'button') {
target = $(target).parents('.btn-del');
}
var video_id = $(target).attr('data-video-id'),
filename = $(target).attr('data-filename');
//console.log('clicked', video_id, filename);
if (confirm('确定删除此视频吗?')) {
$(target).prop('disabled', true);
var datas = {
'id': video_id,
'filename': filename
};
publicAjax(taJian.apis.deleteFav, 'POST', datas, function (data) {
$(target).prop('disabled', false);
if (data.code == 1) {
location.reload();
}else {
alert(data.err);
}
}, function (jqXHR, textStatus, errorThrown) {
$(target).prop('disabled', false);
alert('网络请求失败,请重试。');
});
}
});
}
})();

Loading…
Cancel
Save