rabin před 1 rokem
rodič
revize
d542c15d85
63 změnil soubory, kde provedl 2834 přidání a 736 odebrání
  1. 2 2
      package/manage/index.html
  2. 2 2
      package/manage/sector.html
  3. 1 1
      package/manage/static/css/213.89045697.css
  4. 1 1
      package/manage/static/css/780.4065063c.css
  5. 1 1
      package/manage/static/css/app.d065fbce.css
  6. 0 1
      package/manage/static/css/vab-chunk-c6f52c3a.985f6fb0.css
  7. 1 1
      package/manage/static/css/vab-plugins.f6f5c03c.css
  8. 1 1
      package/manage/static/js/213.bad45f98.js
  9. 1 1
      package/manage/static/js/592.41e25131.js
  10. 1 1
      package/manage/static/js/780.f3d655d2.js
  11. 1 1
      package/manage/static/js/962.faf0c7f7.js
  12. 0 1
      package/manage/static/js/app.151337a9.js
  13. 1 1
      package/manage/static/js/vab-chunk-0b9a05e8.3a2acba3.js
  14. 1 1
      package/manage/static/js/vab-chunk-54e012b3.75c10119.js
  15. 1 1
      package/manage/static/js/vab-chunk-844ca20f.8fd468cb.js
  16. 1 1
      package/manage/static/js/vab-chunk-b3a4eaa1.509e2c01.js
  17. 5 0
      package/manage/static/js/vab-chunk-c6f52c3a.1dffdeec.js
  18. 0 5
      package/manage/static/js/vab-chunk-c6f52c3a.30edfa84.js
  19. 0 5
      package/manage/static/js/vab-plugins.46570d83.js
  20. 5 0
      package/manage/static/js/vab-plugins.863a35d3.js
  21. 86 2
      src/api/api/Video.php
  22. 11 0
      src/api/lib/Template.php
  23. 92 0
      src/api/lib/Video.php
  24. 56 0
      src/api/table/manage/api_video_site.php
  25. 64 0
      src/api/table/manage/api_video_template.php
  26. 36 0
      src/api/table/manage/core.php
  27. 51 0
      src/api/table/site.php
  28. 95 0
      src/api/table/video.php
  29. 20 0
      src/api/table/video_extend.php
  30. 90 0
      src/api/table/video_template.php
  31. 0 139
      src/api/video/Douyin.php
  32. 197 0
      src/api/video/Handle/Clip.php
  33. 9 0
      src/api/video/Handle/Filter/Audio.php
  34. 87 0
      src/api/video/Handle/Filter/Video.php
  35. 82 0
      src/api/video/Remote/Base.php
  36. 159 0
      src/api/video/Remote/Douyin.php
  37. 51 0
      src/source/api/Data.php
  38. 1 49
      src/source/api/Task.php
  39. 94 129
      src/source/api/Test.php
  40. 28 0
      src/source/api/Video.php
  41. 66 0
      src/source/lib/Collection.php
  42. 107 0
      src/source/lib/Content.php
  43. 451 0
      src/source/lib/Data.php
  44. 30 0
      src/source/lib/Datavalue.php
  45. 14 352
      src/source/lib/Info.php
  46. 27 0
      src/source/lib/Value.php
  47. 216 0
      src/source/lib/Video.php
  48. 8 6
      src/source/table/info_data.php
  49. 53 0
      src/source/table/info_data_collection.php
  50. 7 7
      src/source/table/info_data_content.php
  51. 86 0
      src/source/table/info_data_value.php
  52. 129 0
      src/source/table/info_data_video.php
  53. 12 0
      src/source/table/info_value.php
  54. 29 1
      src/source/table/manage/core.php
  55. 53 0
      src/source/table/manage/info_data_file.php
  56. 80 0
      src/source/table/manage/info_data_video.php
  57. 2 1
      src/source/table/manage/info_value.php
  58. 2 0
      src/source/table/manage/source_info.php
  59. 10 8
      src/source/table/manage/source_info_data.php
  60. 31 0
      src/source/table/manage/source_info_data_collection.php
  61. 4 14
      src/source/table/manage/source_info_data_content.php
  62. 64 0
      src/source/table/manage/source_info_data_value.php
  63. 18 0
      src/source/table/manage/source_info_data_video.php

+ 2 - 2
package/manage/index.html

@@ -1,5 +1,5 @@
-<!doctype html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"/><meta content="IE=edge" http-equiv="X-UA-Compatible"/><meta content="webkit" name="renderer"/><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"/><link href="favicon.ico" rel="icon"/><title>Dever Manage</title><meta content="Dever,PHP,PHP框架,轻量,微服务,组件化,接口,低代码,后台,自定义后台,开发,快速开发,AI,人工智能,文档,轻应用,应用,时空之翼,神秘城" name="keywords"/><meta content="rabin@shemic.com" name="author"/><link href="static/css/loading.css?random=2024-05-23 14:24:54-shemic" rel="stylesheet"/><script>document.write(
+<!doctype html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"/><meta content="IE=edge" http-equiv="X-UA-Compatible"/><meta content="webkit" name="renderer"/><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"/><link href="favicon.ico" rel="icon"/><title>Dever Manage</title><meta content="Dever,PHP,PHP框架,轻量,微服务,组件化,接口,低代码,后台,自定义后台,开发,快速开发,AI,人工智能,文档,轻应用,应用,时空之翼,神秘城" name="keywords"/><meta content="rabin@shemic.com" name="author"/><link href="static/css/loading.css?random=2024-05-30 09:35:22-shemic" rel="stylesheet"/><script>document.write(
         "<script type='text/javascript' src='config.js?" +
           Math.random() +
           "'><\/script>"
-      )</script><script defer="defer" src="static/js/app.5a06ff68.js"></script><script defer="defer" src="static/js/213.8c08ea23.js"></script><link href="static/css/app.d065fbce.css" rel="stylesheet"><link href="static/css/213.89045697.css" rel="stylesheet"></head><body><noscript></noscript><div id="app"><div class="first-loading-wrp"><div class="loading-wrp"><span class="dot dot-spin"><i></i> <i></i> <i></i> <i></i></span></div><h1>Dever Manage</h1></div></div></body></html>
+      )</script><script defer="defer" src="static/js/app.151337a9.js"></script><script defer="defer" src="static/js/213.bad45f98.js"></script><link href="static/css/app.d065fbce.css" rel="stylesheet"><link href="static/css/213.89045697.css" rel="stylesheet"></head><body><noscript></noscript><div id="app"><div class="first-loading-wrp"><div class="loading-wrp"><span class="dot dot-spin"><i></i> <i></i> <i></i> <i></i></span></div><h1>Dever Manage</h1></div></div></body></html>

+ 2 - 2
package/manage/sector.html

@@ -1,5 +1,5 @@
-<!doctype html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"/><meta content="IE=edge" http-equiv="X-UA-Compatible"/><meta content="webkit" name="renderer"/><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"/><link href="favicon.ico" rel="icon"/><title>Dever Manage</title><meta content="Dever,PHP,PHP框架,轻量,微服务,组件化,接口,低代码,后台,自定义后台,开发,快速开发,AI,人工智能,文档,轻应用,应用,时空之翼,神秘城" name="keywords"/><meta content="rabin@shemic.com" name="author"/><link href="static/css/loading.css?random=2024-05-23 14:24:54-shemic" rel="stylesheet"/><script>document.write(
+<!doctype html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"/><meta content="IE=edge" http-equiv="X-UA-Compatible"/><meta content="webkit" name="renderer"/><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"/><link href="favicon.ico" rel="icon"/><title>Dever Manage</title><meta content="Dever,PHP,PHP框架,轻量,微服务,组件化,接口,低代码,后台,自定义后台,开发,快速开发,AI,人工智能,文档,轻应用,应用,时空之翼,神秘城" name="keywords"/><meta content="rabin@shemic.com" name="author"/><link href="static/css/loading.css?random=2024-05-30 09:35:22-shemic" rel="stylesheet"/><script>document.write(
         "<script type='text/javascript' src='sector_config.js?" +
           Math.random() +
           "'><\/script>"
-      )</script><script defer="defer" src="static/js/app.5a06ff68.js"></script><script defer="defer" src="static/js/213.8c08ea23.js"></script><link href="static/css/app.d065fbce.css" rel="stylesheet"><link href="static/css/213.89045697.css" rel="stylesheet"></head><body><noscript></noscript><div id="app"><div class="first-loading-wrp"><div class="loading-wrp"><span class="dot dot-spin"><i></i> <i></i> <i></i> <i></i></span></div><h1>Dever Manage</h1></div></div></body></html>
+      )</script><script defer="defer" src="static/js/app.151337a9.js"></script><script defer="defer" src="static/js/213.bad45f98.js"></script><link href="static/css/app.d065fbce.css" rel="stylesheet"><link href="static/css/213.89045697.css" rel="stylesheet"></head><body><noscript></noscript><div id="app"><div class="first-loading-wrp"><div class="loading-wrp"><span class="dot dot-spin"><i></i> <i></i> <i></i> <i></i></span></div><h1>Dever Manage</h1></div></div></body></html>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/css/213.89045697.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/css/780.4065063c.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/css/app.d065fbce.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 1
package/manage/static/css/vab-chunk-c6f52c3a.985f6fb0.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/css/vab-plugins.f6f5c03c.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/213.bad45f98.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/592.41e25131.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/780.f3d655d2.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/962.faf0c7f7.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 1
package/manage/static/js/app.151337a9.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/vab-chunk-0b9a05e8.3a2acba3.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/vab-chunk-54e012b3.75c10119.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/vab-chunk-844ca20f.8fd468cb.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
package/manage/static/js/vab-chunk-b3a4eaa1.509e2c01.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 5 - 0
package/manage/static/js/vab-chunk-c6f52c3a.1dffdeec.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 5
package/manage/static/js/vab-chunk-c6f52c3a.30edfa84.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 5
package/manage/static/js/vab-plugins.46570d83.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 5 - 0
package/manage/static/js/vab-plugins.863a35d3.js


+ 86 - 2
src/api/api/Video.php

@@ -2,8 +2,92 @@
 use Dever;
 class Video
 {
-    public function douyin()
+    # 获取视频基本信息
+    public function remote()
     {
-        return Dever::load('douyin', 'api', 'video')->get();
+        $txt = Dever::input('txt');
+        if (!$txt) {
+            Dever::error('请传入参数');
+        }
+        $type = Dever::input('type');
+        return Dever::load('video', 'api')->remote($txt, $type);
+    }
+
+    # 视频处理
+    public function handle()
+    {
+        $input = Dever::input('input');
+        if (!$input) {
+            Dever::error('请传入参数');
+        }
+        $output = Dever::input('output');
+        $option = array
+        (
+            'time' => array(3, 1),
+            'video' => array
+            (
+                // 去除水印 delogo=x=714:y=28:w=130:h=50
+                # 自带的过滤器
+                /*
+                'drawtext' => array
+                (
+                    'fontfile' => 'test',
+                    'text' => '你好',
+                ),*/
+                //'transpose' => 1,
+                # 定义好的过滤器
+                'text' => array
+                (
+                    '你好',
+                    25,
+                    'white',
+                    50,
+                    50,
+                ),
+            ),
+            /*
+            'filter_complex' => array
+            (
+                '[1:v]chromakey=0x00d810:0.1:0.2[ckout]',
+                '[0:v][ckout]overlay[out]',
+            ),*/
+        );
+        /*
+        $input = array
+        (
+            'file' => $input,
+            'option' => array
+            (
+                'f' => 'lavfi',
+            ),
+        );
+        $output = array
+        (
+            'file' => $input['file'] . '_clip.mp4',
+            'option' => array
+            (
+                # 覆盖
+                'y' => '',
+                # 音频码率
+                'b:a' => '10000k',
+                # 视频码率
+                'b:v' => '10000k',
+                'an' => '',
+                'sn' => '',
+                'vn' => '',
+            ),
+        );*/
+        return Dever::load('video', 'api')->handle($input, $output, $option);
+    }
+
+    # 视频合并
+    public function merge()
+    {
+        $video = Dever::input('video');
+        if (!$video) {
+            Dever::error('请传入参数');
+        }
+        $option = array();
+        return Dever::load('video', 'api')->handle($video, $option);
     }
 }

+ 11 - 0
src/api/lib/Template.php

@@ -0,0 +1,11 @@
+<?php namespace Api\Lib;
+use Dever;
+class Template
+{
+    # 获取官方模板列表
+    public function getList()
+    {
+        $where['status'] = 1;
+        return Dever::db('template', 'api')->select($where);
+    }
+}

+ 92 - 0
src/api/lib/Video.php

@@ -0,0 +1,92 @@
+<?php namespace Api\Lib;
+use Dever;
+class Video
+{
+    # 获取远程视频信息
+    public function remote($content, $type = false, $local = false)
+    {
+        if (!$type) {
+            $type = $this->getType($content);
+        }
+        if (!$type) {
+            Dever::error('不支持的站点类型');
+        }
+        return Dever::load('remote/' . $type, 'api', 'video')->info($content, $local);
+    }
+
+    # 获取本地视频信息
+    public function info($file, $print = false)
+    {
+        return Dever::load('handle/clip', 'api', 'video')->info($file, $print);
+    }
+
+    # 视频处理
+    public function handle($input, $output, $option, $print = false)
+    {
+        if (!$output) {
+            $output = $input . '_clip.mp4';
+        }
+        return Dever::load('handle/clip', 'api', 'video')->act($input, $output, $option, $print);
+    }
+
+    # 视频转码 主要是转换成不同的类型 视频处理也能做到
+    public function convert()
+    {
+
+    }
+
+    # 视频合并
+    public function merge($video, $output = '', $option = array())
+    {
+        $file = array();
+        foreach ($video as $v) {
+            $o = array
+            (
+                'file' => $v . '.ts',
+                //-c copy -bsf:v h264_mp4toannexb -y -f
+                'option' => array
+                (
+                    'c' => 'copy',
+                    'bsf:v' => 'h264_mp4toannexb',
+                    'y' => '',
+                    'f' => 'mpegts',
+                ),
+            );
+            $file[] = Dever::load('handle/clip', 'api', 'video')->act($v, $o);
+        }
+        $input = '"concat:'.implode('|', $file).'"';
+        $output = Dever::file('upload/merge/' . $this->getName($output, 'mp4'));
+        $output = array
+        (
+            'file' => $output,
+            //-c copy -bsf:v h264_mp4toannexb -y -f
+            'option' => array
+            (
+                'y' => '',
+                'an' => '',
+                'sn' => '',
+                'c' => 'copy',
+                'bsf:a' => 'aac_adtstoasc',
+                'movflags' => '+faststart',
+            ),
+        );
+        return Dever::load('handle/clip', 'api', 'video')->act($input, $output, $option);
+    }
+
+    private function getType($content)
+    {
+        if (strstr($content, 'douyin')) {
+            return 'douyin';
+        } elseif (strstr($content, 'xiaohongshu') || strstr($content, 'xhslink')) {
+            return 'xiaohongshu';
+        }
+    }
+
+    protected function getName($name, $ext)
+    {
+        $name = md5($name);
+        $path = array_slice(str_split($name, 2), 0, 3);
+        $dest = implode(DIRECTORY_SEPARATOR, $path) . DIRECTORY_SEPARATOR . $name . '.' . $ext;
+        return $dest;
+    }
+}

+ 56 - 0
src/api/table/manage/api_video_site.php

@@ -0,0 +1,56 @@
+<?php
+return array
+(
+    'source' => 'api/site',
+    'list' => array
+    (
+        'field'      => array
+        (
+            'id',
+            'name' => array
+            (
+                //'tip' => false,
+            ),
+            'key',
+            'sort' => array
+            (
+                'type' => 'input',
+            ),
+            'status' => array
+            (
+                'type' => 'switch',
+                'show'  => '{status}',
+                'active_value' => 1,
+                'inactive_value' => 2,
+            ),
+            'cdate',
+        ),
+        'button' => array
+        (
+            '新增' => array('add'),
+        ),
+        'data_button' => array
+        (
+            '编辑' => 'edit',
+        ),
+        'search'    => array
+        (
+            'status',
+            'name',
+        ),
+    ),
+    'update' => array
+    (
+        'field'    => array
+        (
+            'name' => array
+            (
+                'rules' => true,
+            ),
+            'key' => array
+            (
+                'rules' => true,
+            ),
+        ),
+    ),
+);

+ 64 - 0
src/api/table/manage/api_video_template.php

@@ -0,0 +1,64 @@
+<?php
+return array
+(
+    'source' => 'api/video_template',
+    'list' => array
+    (
+        'field'      => array
+        (
+            'id',
+            'name' => array
+            (
+                //'tip' => false,
+            ),
+            'sort' => array
+            (
+                'type' => 'input',
+            ),
+            'status' => array
+            (
+                'type' => 'switch',
+                'show'  => '{status}',
+                'active_value' => 1,
+                'inactive_value' => 2,
+            ),
+            'cdate',
+        ),
+        'button' => array
+        (
+            '新增' => array('add'),
+        ),
+        'data_button' => array
+        (
+            '编辑' => 'edit',
+        ),
+        'search'    => array
+        (
+            'status',
+            'name',
+        ),
+    ),
+    'update' => array
+    (
+        'field'    => array
+        (
+            'name' => array
+            (
+                'rules' => true,
+            ),
+            'view' => array
+            (
+                'rules' => true,
+            ),
+            'setpts' => array
+            (
+                'rules' => true,
+                'desc' => '这里填写变速倍数的数值,大于1则慢速,大于1则快速',
+            ),
+            'flip' => array
+            (
+                'rules' => true,
+            ),
+        ),
+    ),
+);

+ 36 - 0
src/api/table/manage/core.php

@@ -0,0 +1,36 @@
+<?php
+return array
+(
+    'menu' => array
+    (
+        # 系统相关菜单
+        'api' => array
+        (
+            'name' => '功能',
+            'icon' => 'surgical-mask-line',
+            'sort' => '3',
+            'module' => 'platform',
+        ),
+        'api_video' => array
+        (
+            'parent' => 'api',
+            'name' => '视频管理',
+            'icon' => 'video-chat-line',
+            'sort' => '1',
+        ),
+        'api_video_site' => array
+        (
+            'parent'    => 'api_video',
+            'name'      => '站点管理',
+            'icon'      => 'bug-2-line',
+            'sort'      => '1',
+        ),
+        'api_video_template' => array
+        (
+            'parent'    => 'api_video',
+            'name'      => '视频处理模板',
+            'icon'      => 'video-download-line',
+            'sort'      => '3',
+        ),
+    ),
+);

+ 51 - 0
src/api/table/site.php

@@ -0,0 +1,51 @@
+<?php
+return array
+(
+    'name' => '站点表',
+    'order' => 'sort asc,id asc',
+    'struct' => array
+    (
+        'name' => array
+        (
+            'name'      => '名称',
+            'type'      => 'varchar(50)',
+        ),
+
+        'key' => array
+        (
+            'name'      => '关键词',
+            'type'      => 'varchar(50)',
+        ),
+
+        'status' => array
+        (
+            'name'      => '状态',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '上架',
+                2 => '下架',
+            ),
+        ),
+
+        'sort' => array
+        (
+            'name'      => '排序',
+            'type'      => 'int(11)',
+            'default'   => '1',
+        ),
+    ),
+
+    'default' => array
+    (
+        'field' => 'name,`key`,cdate',
+        'value' => array
+        (
+            '"源代宝","yuandaibao",' . DEVER_TIME,
+            '"抖音","douyin",' . DEVER_TIME,
+            '"快手","kuaishou",' . DEVER_TIME,
+        ),
+        'num' => 1,
+    ),
+);

+ 95 - 0
src/api/table/video.php

@@ -0,0 +1,95 @@
+<?php
+return array
+(
+    'name' => '视频表',
+    //'partition' => 'Dever::call("manage/common.system")',
+    'order' => 'sort asc',
+    'struct' => array
+    (
+        'name' => array
+        (
+            'name'      => '名称',
+            'type'      => 'varchar(200)',
+        ),
+
+        'desc' => array
+        (
+            'name'      => '简介',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'site_id' => array
+        (
+            'name'      => '站点',
+            'type'      => 'int(11)',
+            'value'     => 'api/site',
+            'default'   => 1,
+        ),
+
+        'fid' => array
+        (
+            'name'      => '文件id',
+            'type'      => 'varchar(500)',
+        ),
+
+        'file' => array
+        (
+            'name'      => '视频路径',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'format' => array
+        (
+            'name'      => '类型',
+            'type'      => 'varchar(50)',
+        ),
+
+        'ratio' => array
+        (
+            'name'      => '码率',
+            'type'      => 'varchar(50)',
+        ),
+
+        'height' => array
+        (
+            'name'      => '高度',
+            'type'      => 'int(11)',
+        ),
+
+        'width' => array
+        (
+            'name'      => '宽度',
+            'type'      => 'int(11)',
+        ),
+
+        'duration' => array
+        (
+            'name'      => '时长',
+            'type'      => 'int(11)',
+        ),
+
+        'author' => array
+        (
+            'name'      => '作者',
+            'type'      => 'varchar(100)',
+        ),
+
+        'pdate' => array
+        (
+            'name'      => '发布时间',
+            'type'      => 'int(11)',
+        ),
+        
+        'status' => array
+        (
+            'name'      => '状态',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '上架',
+                2 => '下架',
+            ),
+        ),
+    ),
+);

+ 20 - 0
src/api/table/video_extend.php

@@ -0,0 +1,20 @@
+<?php
+return array
+(
+    'name' => '视频扩展表',
+    //'partition' => 'Dever::call("manage/common.system")',
+    'struct' => array
+    (
+        'vid' => array
+        (
+            'name'      => '视频id',
+            'type'      => 'int(11)',
+        ),
+
+        'remote' => array
+        (
+            'name'      => '远程获取的信息',
+            'type'      => 'text(255)',
+        ),
+    ),
+);

+ 90 - 0
src/api/table/video_template.php

@@ -0,0 +1,90 @@
+<?php
+return array
+(
+    'name' => '视频处理模板',
+    //'partition' => 'Dever::call("manage/common.system")',
+    'order' => 'sort asc,id asc',
+    'struct' => array
+    (
+        'name' => array
+        (
+            'name'      => '名称',
+            'type'      => 'varchar(200)',
+        ),
+
+        'view' => array
+        (
+            'name'      => '展示模式',
+            'type'      => 'tinyint(1)',
+            'default'   => '1',
+            'value'     => array
+            (
+                1 => '不做处理',
+                2 => '模糊分屏',
+                //3 => '上下两屏',
+                //4 => '画中画',
+                //5 => '三分屏',
+            ),
+        ),
+
+        'filter' => array
+        (
+            'name'      => '滤镜',
+            'type'      => 'int(11)',
+            'default'   => '1',
+            'value'     => array
+            (
+                1 => '默认滤镜',
+            ),
+        ),
+
+        'effect' => array
+        (
+            'name'      => '特效',
+            'type'      => 'int(11)',
+            'default'   => '1',
+            'value'     => array
+            (
+                1 => '默认特效',
+            ),
+        ),
+
+        'setpts' => array
+        (
+            'name'      => '变速倍数',
+            'type'      => 'varchar(11)',
+            'default'   => '1',
+        ),
+
+        'flip' => array
+        (
+            'name'      => '镜像',
+            'type'      => 'tinyint(1)',
+            'default'   => '1',
+            'value'     => array
+            (
+                1 => '开启',
+                2 => '关闭',
+            ),
+        ),
+
+        'status' => array
+        (
+            'name'      => '状态',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '可用',
+                2 => '不可用',
+            ),
+        ),
+
+        'sort' => array
+        (
+            'name'      => '排序',
+            'type'      => 'int(11)',
+            'default'   => '1',
+        ),
+    ),
+);

+ 0 - 139
src/api/video/Douyin.php

@@ -1,139 +0,0 @@
-<?php namespace Api\Video;
-use Dever;
-class Douyin
-{
-    private $agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
-    # 下载视频
-    public function get()
-    {
-        //$txt = '3.00 复制打开抖音,看看【电视剧《庆余年》的作品】# 范思辙郭保坤南庆卧龙凤雏 两个显眼包的相遇:相... https://v.douyin.com/i2Tq9R93/ fBT:/ T@l.PX 05/05';
-        //$url = 'https://ansnid.com/douyin/api/?text=https://www.douyin.com/share/video/' . $ids;
-        $txt = Dever::input('txt');
-        if (!$txt) {
-            Dever::error('请传入参数');
-        }
-        if (is_string($txt) && strstr($txt, 'http')) {
-            if (strstr($txt, '抖音')) {
-                $url = \Dever\Helper\Str::getLink($txt);
-                if (!$url || !strstr($url, 'http')) {
-                    Dever::error('链接解析失败');
-                }
-            } else {
-                $url = $txt;
-            }
-            $headers = get_headers($url, 1);
-            $redirect_url = $headers['Location'] ?? $url;
-            if ($redirect_url) {
-                $url = $redirect_url;
-            }
-            // 获取视频id
-            preg_match('/(\d+)/', $url, $matches);
-            if (empty($matches)) {
-                Dever::error('视频id获取失败');
-            }
-            $id = $matches[1];
-        } else {
-            $id = $txt;
-        }
-
-        $params = array(
-            'url' => 'https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id='.$id.'&update_version_code=170400&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=124.0.0.0&browser_online=true&engine_name=Blink&engine_version=124.0.0.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=8&platform=PC',
-            'user_agent' => $this->agent,
-        );
-
-        $client = Dever::data() . 'X-Bogus/client.py';
-        $params = Dever::json_encode($params);
-        putenv('PATH=/usr/local/bin:/usr/bin:/bin');
-        $cmd = "python {$client} " . escapeshellarg($params);
-        exec($cmd, $output, $state);
-        if ($state != 0) {
-            Dever::error('解析错误');
-        }
-        return $this->parse($output[0]);
-    }
-
-    private function parse($url)
-    {
-        $msToken = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 107);
-        $curlHandle = curl_init();
-
-        $header['User-Agent'] = $this->agent;
-        $header['Referer'] = 'https://www.douyin.com/';
-        $header['Cookie'] = 'msToken=' . $msToken . ';odin_tt=324fb4ea4a89c0c05827e18a1ed9cf9bf8a17f7705fcc793fec935b637867e2a5a9b8168c885554d029919117a18ba69;ttwid=1%7CWBuxH_bhbuTENNtACXoesI5QHV2Dt9-vkMGVHSRRbgY%7C1677118712%7C1d87ba1ea2cdf05d80204aea2e1036451dae638e7765b8a4d59d87fa05dd39ff;bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWNsaWVudC1jc3IiOiItLS0tLUJFR0lOIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLVxyXG5NSUlCRFRDQnRRSUJBREFuTVFzd0NRWURWUVFHRXdKRFRqRVlNQllHQTFVRUF3d1BZbVJmZEdsamEyVjBYMmQxXHJcbllYSmtNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVKUDZzbjNLRlFBNUROSEcyK2F4bXAwNG5cclxud1hBSTZDU1IyZW1sVUE5QTZ4aGQzbVlPUlI4NVRLZ2tXd1FJSmp3Nyszdnc0Z2NNRG5iOTRoS3MvSjFJc3FBc1xyXG5NQ29HQ1NxR1NJYjNEUUVKRGpFZE1Cc3dHUVlEVlIwUkJCSXdFSUlPZDNkM0xtUnZkWGxwYmk1amIyMHdDZ1lJXHJcbktvWkl6ajBFQXdJRFJ3QXdSQUlnVmJkWTI0c0RYS0c0S2h3WlBmOHpxVDRBU0ROamNUb2FFRi9MQnd2QS8xSUNcclxuSURiVmZCUk1PQVB5cWJkcytld1QwSDZqdDg1czZZTVNVZEo5Z2dmOWlmeTBcclxuLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tXHJcbiJ9';
-        $header['Accept'] = '*/*';
-        $header['Host'] = 'www.douyin.com';
-        $header['Connection'] = 'keep-alive';
-        $result = Dever::curl($url, array(), 'get', false, $header)->result();
-        if (!$result) {
-            Dever::error('请求错误');
-        }
-        $result = Dever::json_decode($result);
-        if ($result['status_code'] == 0) {
-            // 重新组装需要的视频参数
-            $tags = array();
-            foreach ($result['aweme_detail']['video_tag'] as $v) {
-                $tags[] = $v['tag_name'];
-            }
-            $result = array
-            (
-                'id'       => $result['aweme_detail']['aweme_id'],
-                'comment_gid' => $result['aweme_detail']['comment_gid'],
-                'group_id' => $result['aweme_detail']['group_id'],
-                'is_multi_content' => $result['aweme_detail']['is_multi_content'] ?? false,
-                'is_use_music' => $result['aweme_detail']['is_use_music'],
-                'is_ads' => $result['aweme_detail']['is_ads'],
-                'is_image' => $result['aweme_detail']['is_image_beat'],
-                'media_type' => $result['aweme_detail']['media_type'],
-                'create_time' => $result['aweme_detail']['create_time'],
-                'title' => $result['aweme_detail']['item_title'],
-                'desc' => $result['aweme_detail']['desc'],
-
-                'tags' => $tags,
-                'images' => $result['aweme_detail']['images'] ?? array(),
-                'video' => array
-                (
-                    'cover' => $result['aweme_detail']['video']['origin_cover']['url_list'][0],
-                    'format' => $result['aweme_detail']['video']['format'],
-                    'play_url' => $result['aweme_detail']['video']['play_addr']['url_list'],
-                    'ratio' => $result['aweme_detail']['video']['ratio'],
-                    'height' => $result['aweme_detail']['video']['height'],
-                    'width' => $result['aweme_detail']['video']['width'],
-                    'duration' => $result['aweme_detail']['video']['duration'],
-                    'cdn_url_expired' => $result['aweme_detail']['video']['cdn_url_expired'],
-                ),
-
-                'video_url' => $result['aweme_detail']['video']['play_addr']['url_list'][2],
-                'music'    => array
-                (
-                    'id' => $result['aweme_detail']['music']['id'],
-                    'author' => $result['aweme_detail']['music']['author'],
-                    'avatar' => $result['aweme_detail']['music']['cover_large']['url_list'][0],
-                    'owner_nickname' => $result['aweme_detail']['music']['owner_nickname'],
-                    'title' => $result['aweme_detail']['music']['title'],
-                    'play_url' => $result['aweme_detail']['music']['play_url']['url_list'][0],
-                ),
-
-                'author'    => array
-                (
-                    'uid' => $result['aweme_detail']['author_user_id'],
-                    'avatar' => $result['aweme_detail']['author']['avatar_thumb']['url_list'][0],
-                    'follower_count'    => $result['aweme_detail']['author']['follower_count'],
-                    'nickname'    => $result['aweme_detail']['author']['nickname'],
-                    'sec_uid'    => $result['aweme_detail']['author']['sec_uid'],
-                    'short_id'    => $result['aweme_detail']['author']['short_id'],
-                    'signature'    => $result['aweme_detail']['author']['signature'],
-                    'total_favorited'    => $result['aweme_detail']['author']['total_favorited'],
-                    'unique_id'    => $result['aweme_detail']['author']['unique_id'],
-                    'user_agent'    => $result['aweme_detail']['author']['user_agent'] ?? null,
-                    'verification_type'    => $result['aweme_detail']['author']['verification_type'],
-                    'status'    => $result['aweme_detail']['author']['status'],
-                    'favoriting_count'    => $result['aweme_detail']['author']['favoriting_count'],
-                    'contacts_status'    => $result['aweme_detail']['author']['contacts_status'],
-                    'enterprise_verify_reason'    => $result['aweme_detail']['author']['enterprise_verify_reason'],
-                ),
-            );
-            return $result;
-        }
-        Dever::error('请求错误');
-    }
-}

+ 197 - 0
src/api/video/Handle/Clip.php

@@ -0,0 +1,197 @@
+<?php namespace Api\Video\Handle;
+use Dever;
+use Dever\Helper\Cmd;
+class Clip
+{
+    private $cmd;
+    # ffmpeg [global options] [input file options] -i input_file [output file options] output_file
+    public function info($file, $print = false)
+    {
+        $info = $this->act($file, false, false, $print);
+        $info = implode("\r\n", $info);
+        $data = array();
+         if (preg_match("/Duration: (.*?), start: (.*?), bitrate: (\d*) kb\/s/", $info, $match)) {
+             $data['duration'] = $match[1]; //播放时间
+             $arr_duration = explode(':', $match[1]);
+             $data['seconds'] = $arr_duration[0] * 3600 + $arr_duration[1] * 60 + $arr_duration[2]; //转换播放时间为秒数
+             $data['start'] = $match[2]; //开始时间
+             $data['bitrate'] = $match[3]; //码率(kb)
+         }
+         if (preg_match("/Video: (.*?), (.*?), (.*?), (.*?)[,\s]/", $info, $match)) {
+             $data['vcodec'] = $match[1]; //视频编码格式
+             $data['vformat'] = $match[2]; //视频格式
+             if (strstr($match[3], 'x')) {
+                $data['resolution'] = $match[3]; //视频分辨率
+            } elseif (strstr($match[4], 'x')) {
+                $data['resolution'] = $match[4]; //视频分辨率
+             }
+             $arr_resolution = explode('x', $data['resolution']);
+             $data['width'] = $arr_resolution[0];
+             $data['height'] = $arr_resolution[1];
+         }
+         if (preg_match("/Audio: (\w*), (\d*) Hz/", $info, $match)) {
+             $data['acodec'] = $match[1]; //音频编码
+             $data['asamplerate'] = $match[2]; //音频采样频率
+         }
+         if (isset($data['seconds']) && isset($data['start'])) {
+             $data['play_time'] = $data['seconds'] + $data['start']; //实际播放时间
+         }
+         $data['size'] = filesize($file); //文件大小
+         return $data;
+    }
+    public function act($input, $output, $option = array(), $print = false)
+    {
+        $this->cmd = array('/usr/local/ffmpeg4/bin/ffmpeg');
+        //$this->cmd = array('ffmpeg');
+        $this->cmd[] = $this->input($input);
+        if ($option) {
+            foreach ($option as $k => $v) {
+                if (!is_array($v) || ($k == 'video' || $k == 'audio' || $k == 'filter_complex')) {
+                    $v = array($v);
+                }
+                $this->cmd[] = call_user_func_array(array($this, $k), $v);
+            }
+        }
+        if ($output) {
+            $this->cmd[] = $this->output($output);
+            $shell = $output . '.sh';
+        } else {
+            $shell = $input . '.sh';
+        }
+        $cmd = implode(' ', $this->cmd);
+        if ($print) {
+            print_r($cmd);die;
+        }
+        file_put_contents($shell, 'source /etc/profile && ' . $cmd . ' 2>&1');
+        $cmd = 'chmod +x ' . $shell . ' && ' . $shell . ' && rm -rf ' . $shell;
+        $result = Cmd::shell($cmd, false);
+        if ($result[1]) {
+            if ($output) {
+                return is_array($output) ? $output['file'] : $output;
+            } else {
+                return $result[1];
+            }
+        }
+        return false;
+    }
+
+    # 获取视频
+    public function input($video)
+    {
+        if (is_array($video)) {
+            return $this->option($video['option'] ?? array(), ' -i ' . $video['file']);
+        }
+        return '-i ' . $video;
+    }
+
+    # 输出视频
+    public function output(&$video)
+    {
+        if (is_array($video)) {
+            $result = $this->option($video['option'] ?? array(), ' ' . $video['file']);
+            $video = $video['file'];
+            return $result;
+        }
+        return '-y -b:v 10000k ' . $video;
+    }
+
+    # 按照时间截取
+    public function time($max, $start = 0)
+    {
+        return '-ss ' . gmdate('H:i:s', $start) . ' -t ' . gmdate('H:i:s', $max);
+    }
+
+    # video过滤器
+    public function video($video = array())
+    {
+        if ($video) {
+            $cmd = array();
+            foreach ($video as $k => $v) {
+                $cmd[] = $this->filter('video', $k, $v);
+            }
+            return '-vf "'.implode(',', $cmd).'"';
+        }
+        return '';
+    }
+
+    # audio过滤器
+    public function audio($audio = array())
+    {
+        if ($audio) {
+            $cmd = array();
+            foreach ($audio as $k => $v) {
+                $cmd[] = $this->filter('audio', $k, $v);
+            }
+            return '-af "'.implode(',', $cmd).'"';
+        }
+        return '';
+    }
+
+    # 复杂过滤器
+    public function filter_complex($param)
+    {
+        $cmd = array();
+        foreach ($param as $k => $v) {
+            $cmd[] = $v;
+        }
+        return '-filter_complex "'.implode(';', $cmd).'"';
+    }
+
+    # 过滤器 -简单过滤器
+    public function filter($type, $method, $param)
+    {
+        $class = Dever::load('handle/filter/' . $type, 'api', 'video')->class;
+        if (method_exists($class, $method)) {
+            if (!is_array($param)) {
+                $param = array($param);
+            }
+            return call_user_func_array(array($class, $method), $param);
+        } else {
+            if (is_array($param)) {
+                $tmp = array();
+                foreach ($param as $k => $v) {
+                    if ($v) {
+                        $v = '=' . $v;
+                    }
+                    $tmp[] = $k . $v;
+                }
+                $param = implode(':', $tmp);
+            }
+            if ($param) {
+                $param = '=' . $param;
+            }
+            return $method . $param;
+        }
+    }
+
+    # 获取选项
+    private function option($option, $suffix = '')
+    {
+        $tmp = array();
+        if ($option) {
+            foreach ($option as $k => $v) {
+                if ($v) {
+                    $v = ' ' . $v;
+                }
+                if (is_string($k) && $k) {
+                    $tmp[] = '-' . $this->alias($k) . $v;
+                } else {
+                    $tmp[] = $v;
+                }
+            }
+        }
+        return implode(' ', $tmp) . $suffix;
+    }
+
+    # 设置别名
+    private function alias($k)
+    {
+        if ($k == 'audio') {
+            return 'b:a';
+        }
+        if ($k == 'video') {
+            return 'b:v';
+        }
+        return $k;
+    }
+}

+ 9 - 0
src/api/video/Handle/Filter/Audio.php

@@ -0,0 +1,9 @@
+<?php namespace Api\Video\Handle\Filter;
+use Dever;
+/**
+ * 音频过滤器
+ */
+class Audio
+{
+    private $cmd = '-af';
+}

+ 87 - 0
src/api/video/Handle/Filter/Video.php

@@ -0,0 +1,87 @@
+<?php namespace Api\Video\Handle\Filter;
+use Dever;
+/**
+ * 视频过滤器
+ * 
+ * 变量:
+ *  color    十六进制形式的RGB颜色值:0xRRGGBB [@AA],其中AA的范围是(0,1)中的十进制值或任何有效的颜色名称,如白色,蓝色,黄色等,默认值为黑色,请参见颜色有关详细信息,请参阅FFmpeg基本介绍章节中的名称部分
+width, height   带填充的输出帧的宽度和高度,宽度的值可以从高度导出,反之亦然,两个参数的默认值都是0
+    x, y    输入左上角的坐标(偏移量)与输出帧的左上角有关,两个参数的默认值均为0
+    *   参数的高度,宽度,x, y的表达式的可用变量
+    a   纵横比,与iw/ih相同
+    dar 输入显示宽比,与*sar相同
+    hsub, vsub  水平和垂直的色度子样本值,对于像素格式yuv422p, hsub的值为2,vsub为1
+    in_h, ih    输入的高度
+    in_w, iw    输入的宽度
+    n   输入框的数目,从0开始
+    out_h, oh   输出高度,默认值=高度
+    out_w, ow   输出宽度,默认值=宽度
+    pos 位置在输入框的文件中,如果不知道NAN
+    sar 输入样本比例
+    t   时间戳以秒表示,如果输入时间戳未知
+    x, y    x和y的偏移量由x和y表示,或者NAN如果没有指定
+ */
+class Video
+{
+    # 增加文字水印
+    public function text($text, $fontsize = 25, $fontcolor = 'white', $alpha = 1, $x = 0, $y = 0)
+    {
+        $font = Dever::data() . 'assets/font/PingFang_Bold.ttf';
+        return 'drawtext=fontfile='.$font.':text=\''.$text.'\':fontsize='.$fontsize.':fontcolor='.$fontcolor.':alpha='.$alpha.':shadowy=2:x='.$x.':y='.$y;
+    }
+
+    # 增加文字水印跑马灯
+    public function textmove($text, $fontsize = 25, $fontcolor = 'white', $alpha = 1, $move = 200)
+    {
+        $x = 'if(eq(mod(trunc(t*'.$move.'/(w-text_w))\,2)\,0)\,mod(t*'.$move.'\,w-text_w)\,w-text_w-mod(t*'.$move.'\,w-text_w))';
+        $y = 'if(eq(mod(trunc(t*'.$move.'/(h-text_h))\,2)\,0)\,mod(t*'.$move.'\,h-text_h)\,h-text_h-mod(t*'.$move.'\,h-text_h))';
+        return $this->text($text, $fontsize, $fontcolor, $alpha, $x, $y);
+    }
+
+    # 旋转 0 顺时针旋转90°后再水平翻转 1 顺时针旋转90° 2 逆时针旋转90° 3 逆时针旋转90°后再水平翻转
+    public function transpose($type = 1)
+    {
+        return 'transpose=' . $type;
+    }
+
+    # 翻转 - 镜像 1 水平翻转 2垂直翻转
+    public function flip($type = 1)
+    {
+        return $type == 1 ? 'hflip' : 'vflip';
+    }
+
+    # 填充视频
+    public function pad($width, $height = 0, $x = 0, $y = 0, $color = '')
+    {
+        //width[:height[:x[:y[:color]]]]
+        return 'pad='.$width.':'.$height.':'.$x.':'.$y.':'.$color;
+    }
+
+    # 模糊视频
+    public function boxblur($luma_r, $luma_p)
+    {
+        //boxblur=luma_r:luma_p[:chroma_r:chroma_p[:alpha_r:alpha_p]]
+        return 'boxblur='.$luma_r.':'.$luma_p;
+    }
+
+    # 去水印
+    public function delogo($x, $y, $w, $h)
+    {
+        //delogo=x=714:y=28:w=130:h=50
+        return 'delogo=x=714:y=28:w=130:h=50';
+    }
+
+    # 去马赛克
+    public function minterpolate($mi_mode = 'mci',$mc_mode = 'aobmc', $mb_size = 8)
+    {
+        //minterpolate=mi_mode=mci:mc_mode=aobmc:mb_size=8
+        return 'minterpolate=mi_mode='.$mi_mode.':mc_mode='.$mc_mode.':mb_size=' . $mbsize;
+    }
+
+    # 变速
+    public function setpts($v)
+    {
+        //setpts=2.0*PTS
+        return 'setpts='.$v.'*PTS';
+    }
+}

+ 82 - 0
src/api/video/Remote/Base.php

@@ -0,0 +1,82 @@
+<?php namespace Api\Video\Remote;
+use Dever;
+class Base
+{
+    protected $type = '';
+    protected $local = false;
+    protected $site = array();
+    protected $video = array();
+
+    protected function getVideo($txt, $local)
+    {
+        $this->local = $local;
+        if (!$this->type) {
+            Dever::error('不支持的站点类型');
+        }
+        $this->site = Dever::db('site', 'api')->find(array('key' => $this->type));
+        if (!$this->site) {
+            Dever::error('不支持的站点类型');
+        }
+        if ($this->site['status'] == 2) {
+            Dever::error('不支持的站点类型');
+        }
+        if (is_string($txt) && strstr($txt, 'http')) {
+            $url = \Dever\Helper\Str::getLink($txt);
+            if (!$url || !strstr($url, 'http')) {
+                Dever::error('链接解析失败');
+            }
+            $headers = get_headers($url, 1);
+            $redirect_url = $headers['Location'] ?? $url;
+            if ($redirect_url) {
+                $url = $redirect_url;
+            }
+            // 获取视频id
+            preg_match('/(\d+)/', $url, $matches);
+            if (empty($matches)) {
+                Dever::error('视频id获取失败');
+            }
+            $id = $matches[1];
+        } else {
+            $id = $txt;
+        }
+        if ($this->local) {
+            $data['site_id'] = $this->site['id'];
+            $data['fid'] = $id;
+            $this->video = Dever::db('video', 'api')->find($data);
+            if ($this->video && $this->video['file']) {
+                $extend = Dever::db('video_extend', 'api')->find(array('vid' => $this->video['id']));
+                if ($extend && $extend['remote']) {
+                    $remote = Dever::json_decode($extend['remote']);
+                    $remote['video']['url'] = $this->video['file'];
+                    return $remote;
+                }
+            }
+        }
+        return $id;
+    }
+
+    protected function saveVideo($info)
+    {
+        if ($this->local && !$this->video) {
+            $data['site_id'] = $this->site['id'];
+            $data['fid'] = $info['id'];
+            $data['name'] = $info['title'] ? $info['title'] : $info['desc'];
+            $data['desc'] = $info['desc'];
+            $upload = Dever::load('save', 'upload')->act(3, $info['video']['url'], '', false, md5($data['site_id'] . '_' . $data['fid']));
+            $info['video']['url'] = $upload['url'];
+            $data['file'] = $info['video']['url'];
+            $data['format'] = $info['video']['format'];
+            $data['ratio'] = $info['video']['ratio'];
+            $data['height'] = $info['video']['height'];
+            $data['width'] = $info['video']['width'];
+            $data['duration'] = $info['video']['duration'];
+            $data['author'] = $info['author']['nickname'];
+            $data['pdate'] = $info['cdate'];
+            $info['vid'] = Dever::db('video', 'api')->insert($data);
+            if ($info['vid']) {
+                Dever::db('video_extend', 'api')->insert(array('vid' => $info['vid'], 'remote' => Dever::json_encode($info)));
+            }
+        }
+        return $info;
+    }
+}

+ 159 - 0
src/api/video/Remote/Douyin.php

@@ -0,0 +1,159 @@
+<?php namespace Api\Video\Remote;
+use Dever;
+class Douyin extends Base
+{
+    # 参考 https://github.com/NearHuiwen/TiktokDouyinCrawler
+    protected $type = 'douyin';
+    private $host = 'www.douyin.com';
+    private $msToken = '';
+    private $agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
+    private $odin_tt = '324fb4ea4a89c0c05827e18a1ed9cf9bf8a17f7705fcc793fec935b637867e2a5a9b8168c885554d029919117a18ba69';
+    private $webid = '7246605341814654516';
+    private $ttwid = '1%7C7ZLJzwjjEw7NLeADTpVd-3eId-ZEIg0jpCEzTV9p_2A%7C1677681848%7C4ff4f97328ddc18b6d46c259bc26a05d2e654b50e3f21b27b8f9e9e8f9fcec82';
+    private $bd_ticket_guard_client_data = 'eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWNsaWVudC1jc3IiOiItLS0tLUJFR0lOIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLVxyXG5NSUlCRFRDQnRRSUJBREFuTVFzd0NRWURWUVFHRXdKRFRqRVlNQllHQTFVRUF3d1BZbVJmZEdsamEyVjBYMmQxXHJcbllYSmtNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVKUDZzbjNLRlFBNUROSEcyK2F4bXAwNG5cclxud1hBSTZDU1IyZW1sVUE5QTZ4aGQzbVlPUlI4NVRLZ2tXd1FJSmp3Nyszdnc0Z2NNRG5iOTRoS3MvSjFJc3FBc1xyXG5NQ29HQ1NxR1NJYjNEUUVKRGpFZE1Cc3dHUVlEVlIwUkJCSXdFSUlPZDNkM0xtUnZkWGxwYmk1amIyMHdDZ1lJXHJcbktvWkl6ajBFQXdJRFJ3QXdSQUlnVmJkWTI0c0RYS0c0S2h3WlBmOHpxVDRBU0ROamNUb2FFRi9MQnd2QS8xSUNcclxuSURiVmZCUk1PQVB5cWJkcytld1QwSDZqdDg1czZZTVNVZEo5Z2dmOWlmeTBcclxuLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tXHJcbiJ9';
+
+    # 获取视频信息
+    public function info($txt, $local = false)
+    {
+        //$txt = '3.00 复制打开抖音,看看【电视剧《庆余年》的作品】# 范思辙郭保坤南庆卧龙凤雏 两个显眼包的相遇:相... https://v.douyin.com/i2Tq9R93/ fBT:/ T@l.PX 05/05';
+        //$url = 'https://ansnid.com/douyin/api/?text=https://www.douyin.com/share/video/' . $ids;
+        //https://www.douyin.com/video/7367585269219986723
+        $id = $this->getVideo($txt, $local);
+        if (is_array($id)) {
+            return $id;
+        }
+
+        $url = 'https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id='.$id.'&update_version_code=170400&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=124.0.0.0&browser_online=true&engine_name=Blink&engine_version=124.0.0.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=8&platform=PC';
+
+        $refer = $this->getRefererUrl($id);
+        $result = $this->get($url, $refer);
+
+        if ($result['status_code'] == 0) {
+            // 重新组装需要的视频参数
+            $tags = array();
+            foreach ($result['aweme_detail']['video_tag'] as $v) {
+                $tags[] = $v['tag_name'];
+            }
+            $data = array
+            (
+                'type' => $this->type,
+                'id' => $result['aweme_detail']['aweme_id'],
+                'cdate' => $result['aweme_detail']['create_time'],
+                'title' => $result['aweme_detail']['item_title'],
+                'desc' => $result['aweme_detail']['desc'],
+                'tags' => $tags,
+                'media_type' => $result['aweme_detail']['media_type'],
+
+                'images' => $result['aweme_detail']['images'] ?? array(),
+
+                'video' => array
+                (
+                    'cover' => $result['aweme_detail']['video']['origin_cover']['url_list'][0],
+                    'format' => $result['aweme_detail']['video']['format'],
+                    'url' => $result['aweme_detail']['video']['play_addr']['url_list'][0],
+                    'ratio' => $result['aweme_detail']['video']['ratio'],
+                    'height' => $result['aweme_detail']['video']['height'],
+                    'width' => $result['aweme_detail']['video']['width'],
+                    'duration' => $result['aweme_detail']['video']['duration'],
+                    'cdn_url_expired' => $result['aweme_detail']['video']['cdn_url_expired'],
+                ),
+
+                'music'    => array
+                (
+                    'id' => $result['aweme_detail']['music']['id'],
+                    'author' => $result['aweme_detail']['music']['author'],
+                    'avatar' => $result['aweme_detail']['music']['cover_large']['url_list'][0],
+                    'owner_nickname' => $result['aweme_detail']['music']['owner_nickname'],
+                    'title' => $result['aweme_detail']['music']['title'],
+                    'url' => $result['aweme_detail']['music']['play_url']['url_list'][0],
+                ),
+
+                'author'    => array
+                (
+                    'uid' => $result['aweme_detail']['author_user_id'],
+                    'avatar' => $result['aweme_detail']['author']['avatar_thumb']['url_list'][0],
+                    'follower_count'    => $result['aweme_detail']['author']['follower_count'],
+                    'nickname'    => $result['aweme_detail']['author']['nickname'],
+                    'sec_uid'    => $result['aweme_detail']['author']['sec_uid'],
+                    'short_id'    => $result['aweme_detail']['author']['short_id'],
+                    'signature'    => $result['aweme_detail']['author']['signature'],
+                    'total_favorited'    => $result['aweme_detail']['author']['total_favorited'],
+                    'unique_id'    => $result['aweme_detail']['author']['unique_id'],
+                    'user_agent'    => $result['aweme_detail']['author']['user_agent'] ?? null,
+                    'verification_type'    => $result['aweme_detail']['author']['verification_type'],
+                    'status'    => $result['aweme_detail']['author']['status'],
+                    'favoriting_count'    => $result['aweme_detail']['author']['favoriting_count'],
+                    'contacts_status'    => $result['aweme_detail']['author']['contacts_status'],
+                    'enterprise_verify_reason'    => $result['aweme_detail']['author']['enterprise_verify_reason'],
+                ),
+            );
+            return $this->saveVideo($data);
+        }
+        Dever::error('请求错误');
+    }
+
+    private function get($url, $refer = '')
+    {
+        $this->msToken = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 107);
+        $header['User-Agent'] = $this->agent;
+        $header['Referer'] = 'https://'.$this->host.'/';
+        if ($refer) {
+            $header['Referer'] = $refer;
+            $url .= '&webid=' . $this->webid;
+        }
+        $header['Cookie'] = 'msToken=' . $this->msToken . ';odin_tt=' . $this->odin_tt . ';ttwid=' . $this->ttwid . ';bd_ticket_guard_client_data=' . $this->bd_ticket_guard_client_data;
+        $header['Accept'] = '*/*';
+        $header['Host'] = $this->host;
+        $header['Connection'] = 'keep-alive';
+        
+        $result = Dever::curl($this->url($url), array(), 'get', false, $header)->result();
+        if (!$result) {
+            Dever::error('请求错误');
+        }
+        return Dever::json_decode($result);
+    }
+
+    private function url($url)
+    {
+        $params = array
+        (
+            'type' => 'bogus',
+            'url' => $url,
+            'user_agent' => $this->agent,
+        );
+        $client = Dever::data() . 'douyin/client.py';
+        $params = Dever::json_encode($params);
+        putenv('PATH=/usr/local/bin:/usr/bin:/bin');
+        $cmd = "python3 {$client} " . escapeshellarg($params);
+        exec($cmd, $out, $state);
+        if ($state != 0) {
+            Dever::error('解析错误');
+        }
+        return $out[0];
+    }
+
+    private function getRefererUrl($id)
+    {
+        return false;
+        $refer = 'https://www.douyin.com/discover?modal_id=' . $id;
+        $params = array
+        (
+            'type' => 'webid',
+            'url' => $refer,
+            'user_agent' => $this->agent,
+        );
+        $client = Dever::data() . 'douyin/client.py';
+        $params = Dever::json_encode($params);
+        putenv('PATH=/usr/local/bin:/usr/bin:/bin');
+        $cmd = "python3 {$client} " . escapeshellarg($params);
+        exec($cmd, $out, $state);
+        if ($state != 0) {
+            Dever::error('解析错误');
+        }
+        $temp = str_replace(array("('", "')", "'"), '', $out[0]);
+        $temp = explode(', ', $temp);
+        $this->ttwid = $temp[0];
+        $this->webid = $temp[1];
+        return $refer;
+    }
+}

+ 51 - 0
src/source/api/Data.php

@@ -29,4 +29,55 @@ class Data
         );
         return Dever::load('model/chat', 'ai')->init(1)->completions($msg, true);
     }
+
+    # 链接中转
+    public function location()
+    {
+        $id = Dever::input('id');
+        $info = Dever::db('info_value', 'source')->find($id);
+        if ($info) {
+            $link = $info['value'];
+            if ($link) {
+                header('HTTP/1.1 301 Moved Permanently');
+                header('Location: ' . $link);
+            }
+        }
+        Dever::error('链接错误');
+    }
+
+    # 获取素材的视频模板选择
+    public function getVideoTemplate($value, $set = array())
+    {
+        if (is_array($value)) {
+            $value = end($value);
+        }
+        $result = array();
+        $result['template_id']['show'] = true;
+        $result['template_id']['value'] = '';
+        if (!$value || $value == 3) {
+            $result['template_id']['show'] = false;
+            $result['template_id']['option'] = array();
+            return $result;
+        }
+        $option = $set;
+        if ($set) {
+            $result['template_id']['value'] = -1;
+        }
+        if ($value == 1) {
+            # 官方模板
+            $option = Dever::db('video_template', 'api')->select(array('status' => 1));
+        } else {
+            # 自用模板
+        }
+        $result['template_id']['option'] = $option;
+        return $result;
+    }
+
+    # 删除
+    public function delValue()
+    {
+        $id = Dever::input('id');
+        Dever::db('info_data_value', 'source')->delete($id);
+        return '删除成功';
+    }
 }

+ 1 - 49
src/source/api/Task.php

@@ -6,54 +6,6 @@ class Task
     public function create()
     {
         $id = Dever::input('id');
-        $data = Dever::db('info_data_content', 'source')->find(array('id' => $id));
-        if ($data && $data['ai_platform']) {
-            $data['ai_platform'] = explode(',', $data['ai_platform']);
-            if ($data['ai_platform'][1] == 2) {
-                $method = 'agents';
-                $platform_child = Dever::db('agents', 'ai')->find($data['ai_platform'][2]);
-            } else {
-                $method = 'model';
-                $platform_child = Dever::db('model', 'ai')->find($data['ai_platform'][2]);
-            }
-
-            if ($data['ai_prompt_type'] == 1) {
-                $data['ai_prompt'] = explode(',', $data['ai_prompt']);
-                $prompt = Dever::db('prompt', 'ai')->find($data['ai_prompt'][1]);
-                $content = $prompt['positive'];
-            } else {
-                $content = $data['prompt'];
-            }
-
-            $content .= '。请直接回复生成的内容,文章内容如下:';
-            $info_data = Dever::db('info_data', 'source')->find(array('id' => $data['info_data_id']));
-
-            if ($info_data['type'] == 1) {
-                # 文章
-                if ($info_data['relation'] == 1) {
-                    $info = Dever::db('info', 'source')->find(array('id' => $data['info_id']));
-                    $content .= $info['content'];
-                } else {
-                    $value = Dever::db('info_value', 'source')->find(array('id' => $info_data['relation_id']));
-                    $content .= $value['content'];
-                }
-
-                $msg = array();
-                $msg[] = array
-                (
-                    'role' => 'user',
-                    'content' => $content,
-                );
-
-                $content = Dever::load($method . '/chat', 'ai')->init($platform_child['id'])->completions($msg);
-
-                if ($content) {
-                    $content = str_replace("\n", '<br />', $content);
-                    Dever::db('info_data_content', 'source')->update(array('id' => $data['id']), array('status' => 3, 'content' => $content));
-                }
-            }
-        }
-        
-        return '完成';
+        return Dever::load('content', 'source')->create($id);
     }
 }

+ 94 - 129
src/source/api/Test.php

@@ -2,136 +2,101 @@
 use Dever;
 class Test
 {
-    private $agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
-    # 下载视频
-    public function get()
+    public function ffmpeg()
     {
-        //$txt = '3.00 复制打开抖音,看看【电视剧《庆余年》的作品】# 范思辙郭保坤南庆卧龙凤雏 两个显眼包的相遇:相... https://v.douyin.com/i2Tq9R93/ fBT:/ T@l.PX 05/05';
-        //$url = 'https://ansnid.com/douyin/api/?text=https://www.douyin.com/share/video/' . $ids;
-        $txt = Dever::input('txt');
-        if (!$txt) {
-            Dever::error('请传入参数');
-        }
-        if (is_string($txt) && strstr($txt, 'http')) {
-            if (strstr($txt, '抖音')) {
-                $url = \Dever\Helper\Str::getLink($txt);
-                if (!$url || !strstr($url, 'http')) {
-                    Dever::error('链接解析失败');
-                }
-            }
-            $headers = get_headers($url, 1);
-            $redirect_url = $headers['Location'] ?? $url;
-            if ($redirect_url) {
-                $url = $redirect_url;
-            }
-            // 获取视频id
-            preg_match('/(\d+)/', $url, $matches);
-            if (empty($matches)) {
-                Dever::error('视频id获取失败');
-            }
-            $id = $matches[1];
-        } else {
-            $id = $txt;
-        }
-
-        $params = array(
-            'url' => 'https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id='.$id.'&update_version_code=170400&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=124.0.0.0&browser_online=true&engine_name=Blink&engine_version=124.0.0.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=8&platform=PC',
-            'user_agent' => $this->agent,
-        );
-
-        $client = Dever::data() . 'X-Bogus/client.py';
-        $params = Dever::json_encode($params);
-        putenv('PATH=/usr/local/bin:/usr/bin:/bin');
-        $cmd = "python {$client} " . escapeshellarg($params);
-        exec($cmd, $output, $state);
-        if ($state != 0) {
-            Dever::error('解析错误');
-        }
-        return $this->parse($output[0]);
-    }
+        $start = '02';
+        $end = '03';
+        $text = '哈哈哈';
+        $x = 50;
+        $y = 50;
+        $fontsize = 25;
+        $file = '1.mp4';
+        $ts_file = str_replace('1.mp4', '10.mp4', $file);
 
-    private function parse($url)
-    {
-        $msToken = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 107);
-        $curlHandle = curl_init();
-
-        $header['User-Agent'] = $this->agent;
-        $header['Referer'] = 'https://www.douyin.com/';
-        $header['Cookie'] = 'msToken=' . $msToken . ';odin_tt=324fb4ea4a89c0c05827e18a1ed9cf9bf8a17f7705fcc793fec935b637867e2a5a9b8168c885554d029919117a18ba69;ttwid=1%7CWBuxH_bhbuTENNtACXoesI5QHV2Dt9-vkMGVHSRRbgY%7C1677118712%7C1d87ba1ea2cdf05d80204aea2e1036451dae638e7765b8a4d59d87fa05dd39ff;bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWNsaWVudC1jc3IiOiItLS0tLUJFR0lOIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLVxyXG5NSUlCRFRDQnRRSUJBREFuTVFzd0NRWURWUVFHRXdKRFRqRVlNQllHQTFVRUF3d1BZbVJmZEdsamEyVjBYMmQxXHJcbllYSmtNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVKUDZzbjNLRlFBNUROSEcyK2F4bXAwNG5cclxud1hBSTZDU1IyZW1sVUE5QTZ4aGQzbVlPUlI4NVRLZ2tXd1FJSmp3Nyszdnc0Z2NNRG5iOTRoS3MvSjFJc3FBc1xyXG5NQ29HQ1NxR1NJYjNEUUVKRGpFZE1Cc3dHUVlEVlIwUkJCSXdFSUlPZDNkM0xtUnZkWGxwYmk1amIyMHdDZ1lJXHJcbktvWkl6ajBFQXdJRFJ3QXdSQUlnVmJkWTI0c0RYS0c0S2h3WlBmOHpxVDRBU0ROamNUb2FFRi9MQnd2QS8xSUNcclxuSURiVmZCUk1PQVB5cWJkcytld1QwSDZqdDg1czZZTVNVZEo5Z2dmOWlmeTBcclxuLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tXHJcbiJ9';
-        $header['Accept'] = '*/*';
-        $header['Host'] = 'www.douyin.com';
-        $header['Connection'] = 'keep-alive';
-        $result = Dever::curl($url, array(), 'get', false, $header)->result();
-        if (!$result) {
-            Dever::error('请求错误');
-        }
-        $result = Dever::json_decode($result);
-        if ($result['status_code'] == 0) {
-            // 重新组装需要的视频参数
-            $tags = array();
-            foreach ($result['aweme_detail']['video_tag'] as $v) {
-                $tags[] = $v['tag_name'];
-            }
-            $result = array
-            (
-                'id'       => $result['aweme_detail']['aweme_id'],
-                'comment_gid' => $result['aweme_detail']['comment_gid'],
-                'group_id' => $result['aweme_detail']['group_id'],
-                'is_multi_content' => $result['aweme_detail']['is_multi_content'] ?? false,
-                'is_use_music' => $result['aweme_detail']['is_use_music'],
-                'is_ads' => $result['aweme_detail']['is_ads'],
-                'is_image' => $result['aweme_detail']['is_image_beat'],
-                'media_type' => $result['aweme_detail']['media_type'],
-                'create_time' => $result['aweme_detail']['create_time'],
-                'title' => $result['aweme_detail']['item_title'],
-                'desc' => $result['aweme_detail']['desc'],
-
-                'tags' => $tags,
-                'images' => $result['aweme_detail']['images'] ?? array(),
-                'video' => array
-                (
-                    'cover' => $result['aweme_detail']['video']['origin_cover']['url_list'][0],
-                    'format' => $result['aweme_detail']['video']['format'],
-                    'play_url' => $result['aweme_detail']['video']['play_addr']['url_list'],
-                    'ratio' => $result['aweme_detail']['video']['ratio'],
-                    'height' => $result['aweme_detail']['video']['height'],
-                    'width' => $result['aweme_detail']['video']['width'],
-                    'duration' => $result['aweme_detail']['video']['duration'],
-                    'cdn_url_expired' => $result['aweme_detail']['video']['cdn_url_expired'],
-                ),
-
-                'video_url' => $result['aweme_detail']['video']['play_addr']['url_list'][2],
-                'music'    => array
-                (
-                    'id' => $result['aweme_detail']['music']['id'],
-                    'author' => $result['aweme_detail']['music']['author'],
-                    'avatar' => $result['aweme_detail']['music']['cover_large']['url_list'][0],
-                    'owner_nickname' => $result['aweme_detail']['music']['owner_nickname'],
-                    'title' => $result['aweme_detail']['music']['title'],
-                    'play_url' => $result['aweme_detail']['music']['play_url']['url_list'][0],
-                ),
-
-                'author'    => array
-                (
-                    'uid' => $result['aweme_detail']['author_user_id'],
-                    'avatar' => $result['aweme_detail']['author']['avatar_thumb']['url_list'][0],
-                    'follower_count'    => $result['aweme_detail']['author']['follower_count'],
-                    'nickname'    => $result['aweme_detail']['author']['nickname'],
-                    'sec_uid'    => $result['aweme_detail']['author']['sec_uid'],
-                    'short_id'    => $result['aweme_detail']['author']['short_id'],
-                    'signature'    => $result['aweme_detail']['author']['signature'],
-                    'total_favorited'    => $result['aweme_detail']['author']['total_favorited'],
-                    'unique_id'    => $result['aweme_detail']['author']['unique_id'],
-                    'user_agent'    => $result['aweme_detail']['author']['user_agent'] ?? null,
-                    'verification_type'    => $result['aweme_detail']['author']['verification_type'],
-                    'status'    => $result['aweme_detail']['author']['status'],
-                    'favoriting_count'    => $result['aweme_detail']['author']['favoriting_count'],
-                    'contacts_status'    => $result['aweme_detail']['author']['contacts_status'],
-                    'enterprise_verify_reason'    => $result['aweme_detail']['author']['enterprise_verify_reason'],
-                ),
-            );
-            return $result;
-        }
-        Dever::error('请求错误');
+        //$command[] = 'ffmpeg -ss 00:00:'.$start.' -t 00:00:'.$end.' -y -i '.$file.' -vf "drawtext=fontfile=/share/lib/php/dever_package/poster/fonts/PingFang_Bold.ttf: text=\''.$text.'\':x='.$x.':y='.$y.':fontsize='.$fontsize.':fontcolor=white:shadowy=2" -c copy -bsf:v h264_mp4toannexb -an -f mpegts ' . $ts_file;
+
+        $command[] = '/usr/local/ffmpeg/bin/ffmpeg -ss 00:00:'.$start.' -t 00:00:'.$end.' -y -i '.$file.' -an -vn -sn -vf "drawtext=fontfile=/share/lib/php/dever_package/poster/fonts/PingFang_Bold.ttf: text=\''.$text.'\':x='.$x.':y='.$y.':fontsize='.$fontsize.':fontcolor=white:shadowy=2" -b:v 10000k ' . $ts_file;
+
+        $convert[] = $ts_file;
+
+        $file = '2.mp4';
+        $ts_file = str_replace('2.mp4', '20.mp4', $file);
+
+        //$command[] = 'ffmpeg -ss 00:00:'.$start.' -t 00:00:'.$end.' -y -i '.$file.' -c copy -bsf:v h264_mp4toannexb -an -f mpegts ' . $ts_file;
+
+        $command[] = '/usr/local/ffmpeg/bin/ffmpeg -ss 00:00:'.$start.' -t 00:00:'.$end.' -y -i '.$file.' -an -vf "drawtext=fontfile=/share/lib/php/dever_package/poster/fonts/PingFang_Bold.ttf: text=\''.$text.'\':x='.$x.':y='.$y.':fontsize='.$fontsize.':fontcolor=white:shadowy=2" -b:v 10000k ' . $ts_file;
+
+        $convert[] = $ts_file;
+        $output = 'test.mp4';
+
+        $command[] = '/usr/local/ffmpeg/bin/ffmpeg -y -i "concat:'.implode('|', $convert).'" -acodec copy -vcodec copy -bsf:a aac_adtstoasc -movflags +faststart ' . $output;
+
+        $command = implode(' && ', $command);
+
+        print_r($command);die;
+
+        /*
+        视频合并
+        ffmpeg -i 10.mp4 -c copy -bsf:v h264_mp4toannexb -y -f mpegts 10.ts
+        ffmpeg -i 20.mp4 -c copy -bsf:v h264_mp4toannexb -y -f mpegts 20.ts
+        ffmpeg -i "concat:10.ts|20.ts" -y -c copy -an -bsf:a aac_adtstoasc -movflags +faststart ts.mp4
+
+        # 淡入淡出
+        ffmpeg -i 1.mp4 -vf fade=in:0:90 out.mp4
+
+        # 镜像
+        ffmpeg -i 1.mp4 -vf "hflip" outut.mp4
+
+
+        ffmpeg -i 1.mp4 -i 2.mp4 -filter_complex "[0:v]fade=t=out:st=3:d=1[vf];[1:v]fade=t=in:st=0:d=1[vf1];[vf][vf1]overlay=enable='between(t,3,4)'[v]" -map "[v]" -map 0:a output.mp4
+
+
+        # 等比缩放+模糊背景
+
+        # 同一个视频分开为a,b 两个流
+        # a -> 按照308:544(544/960比例)截取视频 -> e -> 使用模糊效果 -> f -> 放大到544/960 尺寸 -> 1
+        # b -> 缩小到544/-1(544/960比例) 尺寸 -> 2
+        # 将 2 放到 1 的视频中间。
+
+        ffmpeg -i 1.mp4 -vf "split[a][b];[a]crop=ih*(544/960):ih[e];[e]boxblur=5:5[f];[f]scale=544:960[1];[b]scale=544:308[2];[1][2]overlay=0:326" {commonFFmpegArg} -y 10.mp4
+
+        ffmpeg -i /video_splicing/2077357_0_55257.ts -vf scale=544:-1,pad=544:960:0:326:black {commonFFmpegArg} -y /video_splicing/2077357_0_55257_scale.ts
+
+
+        竖屏转横屏
+        ffmpeg -i 1.mp4 -metadata:s:v rotate="0" -c:v copy -c:a copy 10.mp4
+        ffmpeg -i 10.mp4  -vf "transpose=1,transpose=2"  out.mp4
+
+
+        ffmpeg -i 2.mp4 -vf "transpose=1" -c:a copy 20.mp4
+        ffmpeg -i 20.mp4 -vf scale=1920:1080,setsar=1:1 -c:a copy 200.mp4
+
+        ffmpeg -i 200.mp4 -filter_complex "[0:v]split[vleft][vtmp];[vtmp]crop=iw/2:ih:ow-iw/2:0,boxblur=luma_radius=min(h\,w)/20:luma_power=1:chroma_radius=min(cw,ch)/20:chroma_power=1[crop_left];[vleft][crop_left]hstack" -c:a copy 2out.mp4
+
+
+        ffmpeg -i 1.mp4 -vf "drawtext=fontfile=arial.ttf:text='welcome':x=(w-tw)/2:y=(h-th)/2" output.mp4
+
+        ffmpeg -i 1.mp4 -i 2.mp4 -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" output.mp4
+
+        ffmpeg -i 1.mp4 -vf "drawtext=text='@小新小新':fontfile=/share/lib/php/dever_package/poster/fonts/PingFang_Bold.ttf:fontsize=30:y=h/2:x=w-tw-mod(t*50\, w-tw)" output.mp4
+
+        ffmpeg -i 1.mp4 -vf "drawtext=text=string1 string2 string3 string4 string5 string6 string7 :expansion=normal:fontfile=foo.ttf: y=h-line_h-10:x=(mod(5*n\,w+tw)-tw): fontcolor=white: fontsize=40: shadowx=2: shadowy=2" output.mp4
+
+
+        ffmpeg -i 1.mp4 -vf "drawtext=fontfile=/share/lib/php/dever_package/poster/fonts/PingFang_Bold.ttf:fontsize=30:text='@我是鱼小新':x=0:y=h-th:enable='between(t,0,10)',drawtext=fontfile=arial.ttf:fontsize=30:text='Hello,World':x=w-tw:y=0:enable='between(t,10,20)'" output.mp4
+
+        上下对称,水面效果
+        ffmpeg -i 1.mp4 -filter_complex "[0:v]pad=h=2*ih[a];[0:v]vflip[b];[a][b]overlay=y=h" duichen3.mp4 -y
+
+        左右对称,镜面效果
+        ffmpeg -i 1.mp4 -filter_complex "[0:v]pad=w=2*iw[a];[0:v]hflip[b];[a][b]overlay=x=w" duichen2.mp4 -y
+
+        //将视频的上半部分翻转,并覆盖在下半部分的区域
+        ffmpeg -i 4.mp4 -vf "split [main][tmp];[tmp] crop=iw:ih/2:0:0, vflip [flip];[main][flip] overlay=0:H/2" 44.mp4 -y
+
+        随机跑马灯
+        ffmpeg -i input.mp4 -vf "drawtext=text='hello,mokev.com':fontfile=font.ttf:fontsize=24:fontcolor=white:x=if(eq(mod(trunc(t*50/(w-text_w))\,2)\,0)\,mod(t*50\,w-text_w)\,w-text_w-mod(t*50\,w-text_w)):y=if(eq(mod(trunc(t*50/(h-text_h))\,2)\,0)\,mod(t*50\,h-text_h)\,h-text_h-mod(t*50\,h-text_h))" output.mp4
+
+        */
     }
 }

+ 28 - 0
src/source/api/Video.php

@@ -0,0 +1,28 @@
+<?php namespace Source\Api;
+use Dever;
+class Video
+{
+    # 查看详情
+    public function view()
+    {
+        $id = Dever::input('id');
+        $type = Dever::input('type');
+        $info = Dever::db($type, 'source')->find($id);
+
+        $html = '<table align="center" border="1" cellpadding="10" cellspacing="5" width="1000">
+        <tr height="50">
+            <td>原视频</td>
+            <td><video controls loop width="500"><source src="'.$info['value'].'" /></video></td>
+        </tr>
+        <tr>
+            <td>新视频</td>
+            <td><video controls loop width="500"><source src="'.$info['file'].'" /></video></td>
+        </tr>
+        <tr>
+            <td>内容</td>
+            <td>'.$info['content'].'</td>
+        </tr>
+    </table>';
+        echo $html;die;
+    }
+}

+ 66 - 0
src/source/lib/Collection.php

@@ -0,0 +1,66 @@
+<?php namespace Source\Lib;
+use Dever;
+class Collection
+{
+    public function update($data)
+    {
+        $info_data = Dever::db('info_data', 'source')->find($data['info_data_id']);
+        if ($info_data['relation'] == 2) {
+            $info_data_list = Dever::db('info_data', 'source')->select(array('info_id' => $info_data['info_id'], 'type' => 3, 'status' => 1));
+            if (!$info_data_list) {
+                # commit 不管用,暂时先删除吧
+                Dever::db('info_data_collection', 'source')->delete($data['id']);
+                Dever::error('暂无短视频素材');
+            }
+        } elseif ($info_data['relation'] == 3) {
+            $info_data_list = Dever::db('info_data_value', 'source')->select(array('info_data_id' => $info_data['id'], 'status' => 1));
+            if (!$info_data_list) {
+                Dever::db('info_data_collection', 'source')->delete($data['id']);
+                Dever::error('暂无短视频素材');
+            }
+        }
+        
+        $video = array();
+        foreach ($info_data_list as $k => $v) {
+            if (!isset($v['file'])) {
+                $data_video = Dever::db('info_data_video', 'source')->find(array('info_data_id' => $v['id']));
+                $v['file'] = $data_video['file'] ?? false;
+            } else {
+                $info_data_temp = Dever::db('info_data', 'source')->find($v['info_data_id']);
+                $v['relation_id'] = $info_data_temp;
+            }
+            
+            if ($v['file']) {
+                $video[] = Dever::load('view', 'upload')->local($v['file']);
+                continue;
+            }
+            $info_value = Dever::db('info_value', 'source')->find($v['relation_id']);
+            if ($info_value && $info_value['value']) {
+                $video[] = Dever::load('view', 'upload')->local($info_value['value']);
+            }
+        }
+
+        if ($video) {
+            $file = Dever::load('video', 'api')->merge($video, $data['id']);
+            $file = Dever::load('view', 'upload')->http($file);
+            Dever::db('info_data_collection', 'source')->update(array('id' => $data['id']), array('file' => $file));
+        }
+        return $data;
+    }
+
+    public function getText($text, $data)
+    {
+        if (strpos($text, '{') !== false && strpos($text, '{"') === false) {
+            $func = function ($r) use ($data) {
+                if (isset($data[$r[1]])) {
+                    return $data[$r[1]];
+                }
+                return false;
+            };
+            $text = preg_replace_callback('/{(.*?)}/', $func, $text);
+        }
+        $eval = '$text = "' . $text . '";';
+        @eval($eval);
+        return $text;
+    }
+}

+ 107 - 0
src/source/lib/Content.php

@@ -0,0 +1,107 @@
+<?php namespace Source\Lib;
+use Dever;
+class Content
+{
+    public function update($data)
+    {
+        Dever::db('info_data', 'source')->update(array('id' => $data['info_data_id']), array('info_data_content_id' => $data['id']));
+        if (isset($data['ai_platform'])) {
+            $info = '更新智能生成';
+            if ($data['ai_platform']) {
+                if (is_string($data['ai_platform'])) {
+                    $data['ai_platform'] = explode(',', $data['ai_platform']);
+                }
+                $platform = Dever::db('platform', 'ai')->find($data['ai_platform'][0]);
+                if ($data['ai_platform'][1] == 2) {
+                    $platform_type = '智能体';
+                    $platform_child = Dever::db('agents', 'ai')->find($data['ai_platform'][2]);
+                } else {
+                    $platform_type = '模型';
+                    $platform_child = Dever::db('model', 'ai')->find($data['ai_platform'][2]);
+                }
+                $info .= ',智能平台:' . $platform['name'] . '-' . $platform_type . '-' . $platform_child['name'];
+            }
+            $info .= ' / 提示词类型:' . ($data['ai_prompt_type'] == 1 ? '通用提示词' : '自定义');
+            if ($data['ai_prompt_type'] == 1) {
+                if (is_string($data['ai_prompt'])) {
+                    $data['ai_prompt'] = explode(',', $data['ai_prompt']);
+                }
+                $prompt = Dever::db('prompt', 'ai')->find($data['ai_prompt'][1]);
+                $info .= ' / 提示词:' . $prompt['name'];
+            } else {
+                $info .= ' / 提示词:' . $data['prompt'];
+            }
+            
+            Dever::load('log', 'source')->add($data['info_data_id'], $info);
+            # 更改当前素材状态
+            Dever::db('info_data_content', 'source')->update(array('id' => $data['id']), array('status' => 2));
+            # 提交生成任务
+            \Dever\Helper\Cmd::run('task.create', array('id' => $data['id'], 'authorization' => \Dever\Helper\Str::encode(\Dever\Helper\Env::header('authorization'))), 'source');
+            //$this->create($data['id']);
+        }
+        if (isset($data['file'])) {
+            $info = '手动修改文案';
+            if ($data['file']) {
+                $info = ',更新文件:' . $data['file'];
+            }
+            if ($data['content']) {
+                $info = ',更新文案';
+            }
+            Dever::load('log', 'source')->add($data['info_data_id'], $info);
+            # 更改当前素材状态
+            Dever::db('info_data_content', 'source')->update(array('id' => $data['id']), array('status' => 3));
+        }
+        
+        return $data;
+    }
+
+    public function create($id)
+    {
+        $data = Dever::db('info_data_content', 'source')->find(array('id' => $id));
+        if ($data && $data['ai_platform']) {
+            $data['ai_platform'] = explode(',', $data['ai_platform']);
+            if ($data['ai_platform'][1] == 2) {
+                $method = 'agents';
+                $platform_child = Dever::db('agents', 'ai')->find($data['ai_platform'][2]);
+            } else {
+                $method = 'model';
+                $platform_child = Dever::db('model', 'ai')->find($data['ai_platform'][2]);
+            }
+
+            if ($data['ai_prompt_type'] == 1) {
+                $data['ai_prompt'] = explode(',', $data['ai_prompt']);
+                $prompt = Dever::db('prompt', 'ai')->find($data['ai_prompt'][1]);
+                $content = $prompt['positive'];
+            } else {
+                $content = $data['prompt'];
+            }
+
+            $content .= '。请直接回复生成的内容,文章内容如下:';
+            $info_data = Dever::db('info_data', 'source')->find(array('id' => $data['info_data_id']));
+
+            # 文章
+            if ($info_data['relation'] == 1) {
+                $info = Dever::db('info', 'source')->find(array('id' => $data['info_id']));
+                $content .= $info['content'];
+            } else {
+                $value = Dever::db('info_value', 'source')->find(array('id' => $info_data['relation_id']));
+                $content .= $value['content'];
+            }
+
+            $msg = array();
+            $msg[] = array
+            (
+                'role' => 'user',
+                'content' => $content,
+            );
+
+            $content = Dever::load($method . '/chat', 'ai')->init($platform_child['id'])->completions($msg);
+
+            if ($content) {
+                $content = str_replace("\n", '<br />', $content);
+                Dever::db('info_data_content', 'source')->update(array('id' => $data['id']), array('status' => 3, 'content' => $content));
+            }
+        }
+        return 'ok';
+    }
+}

+ 451 - 0
src/source/lib/Data.php

@@ -0,0 +1,451 @@
+<?php namespace Source\Lib;
+use Dever;
+class Data
+{
+    public function updateStart($data)
+    {
+        if (isset($data['content_version']) && $data['content_version']) {
+            if ($data['content_version'] <= 0) {
+                $data['info_data_content_id'] = 0;
+            } else {
+                $data['info_data_content_id'] = $data['content_version'];
+            }
+            unset($data['content_version']);
+        }
+        return $data;
+    }
+
+    public function update($data)
+    {
+        $db = Dever::db('info_data', 'source');
+        $relation_id = Dever::call("source/value.getList", array($data['info_id']));
+        $info = '更新素材配置:' . $data['name'] . ' / 素材类型:' . $db->value('type', $data) . ' / 读取资源:' . $db->value('relation', $data) . ' / 资源附件:' . ($data['relation'] == 2 ? Dever::in_array($relation_id, $data['relation_id']) : '-');
+        if (isset($data['info_data_content_id'])) {
+            if ($data['info_data_content_id'] > 0) {
+                $content = Dever::db('info_data_content', 'source')->find($data['info_data_content_id']);
+                $info .= ' / 文案版本:' . date('YmdHis', $content['cdate']);
+            } else {
+                $info .= ' / 文案版本:新增文案';
+            }
+        }
+        Dever::load('log', 'source')->add($data['id'], $info);
+        return $data;
+    }
+
+    # 获取素材详情页
+    public function getView($page)
+    {
+        # 这里获取基本信息
+        $db = Dever::db('info_data', 'source');
+        $relation_link = Dever::url('source/data.location', array('id' => $page->info['relation_id']), true);
+        $content = array
+        (
+            array
+            (
+                'name' => '素材类型',
+                # 类型,text普通文本,tag标签,link链接,image图片 progress进度条 stat统计 timeline时间线 table表格
+                'type' => 'text',
+                'content' => $db->value('type', $page->info),
+                # 样式primary success warning danger info exception
+                'style' => 'primary',
+            ),
+            array
+            (
+                'name' => '读取资源',
+                'type' => 'text',
+                'content' => $db->value('relation', $page->info),
+                'style' => 'primary',
+            ),
+            array
+            (
+                'name' => '资源附件',
+                'type' => 'text',
+                'content' => $page->info['relation'] == 2 ? '<a href="'.$relation_link.'" target="_blank">' . Dever::in_array(Dever::call("source/value.getList", array($page->info['info_id'])), $page->info['relation_id']) . '</a>' : '-',
+                'style' => 'primary',
+            ),
+        );
+
+        $button = array();
+        $button[] = array
+        (
+            'name' => '修改配置',
+            # fastedit、fastadd、oper、api、link、route
+            'type' => 'fastedit',
+            'path' => 'set_zone_source/source_info_data',
+            'row' => array
+            (
+                'id' => $page->info['id'],
+            ),
+            'param' => array
+            (
+                'info_id' => $page->info['info_id'],
+            ),
+        );
+
+        $value = array();
+        $media = false;
+        if ($page->info['relation'] == 2) {
+            $data_value_where['info_id'] = $page->info['info_id'];
+            if ($page->info['relation_id'] > 0) {
+                $data_value_where['id'] = $page->info['relation_id'];
+            }
+            $data_value = Dever::db('info_value', 'source')->find($data_value_where);
+            if ($data_value) {
+                $media = true;
+            }
+        } elseif ($page->info['relation'] == 3) {
+            $value = $this->getViewValue($page->info['id'], $content, $button, $media);
+        }
+
+        if ($media) {
+            if ($page->info['type'] == 3) {
+                $this->getViewMedia('视频', 'video', $page->info['id'], $content, $button);
+            } elseif ($page->info['type'] == 10) {
+                $data = $this->getViewMedia('合集', 'collection', $page->info['id'], $content, $button);
+                if ($data) {
+                    $this->getViewMedia('合集视频', 'video', $page->info['id'], $content, $button);
+                }
+            }
+        }
+
+        $text = $this->getViewText($page->info['info_data_content_id'], $page->info['id'], $content, $button);
+
+        $button[] = array
+        (
+            'name' => '分发设置',
+            'type' => 'fastedit',
+            'path' => 'platform/role',
+            'row' => array
+            (
+                'id' => $page->info['id'],
+            ),
+        );
+
+        $info[] = array
+        (
+            # 类型,desc描述 table表格,表格有head和body即可
+            'type' => 'desc',
+            'name' => $page->info['name'],
+            # 每行展示数量
+            'column' => 1,
+            # 是否有边框
+            'border' => true,
+            # 排列方向:horizontal横向 vertical纵向
+            'direction' => 'horizontal',
+            # 右侧按钮
+            'button' => $button,
+            # 具体内容
+            'content' => $content,
+        );
+
+        $tab = array
+        (
+            'active' => 'text',
+            'content' => array(),
+        );
+        if ($value) {
+            $tab['active'] = 'value';
+            $tab['content']['value'] = $value;
+        }
+        $tab['content']['text'] = $text;
+        //$tab['content']['log'] = $this->getViewLog($page->info['id']);
+        
+        return array('info' => $info, 'tab' => $tab);
+    }
+
+    public function getViewMedia($name, $table, $data_id, &$content, &$button)
+    {
+        $data = Dever::db('info_data_' . $table, 'source')->find(array('info_data_id' => $data_id));
+        $type = 'fastadd';
+        $id = 0;
+        if ($data) {
+            $type = 'fastedit';
+            $id = $data['id'];
+            if ($data['file']) {
+                $content[] = array
+                (
+                    'name' => $name . '链接',
+                    'type' => 'text',
+                    'content' => '<a href="'.$data['file'].'" target="_blank">' . $data['file'] . '</a>',
+                    'style' => 'primary',
+                );
+            }
+        }
+        $button[] = array
+        (
+            'name' => $name . '处理',
+            'type' => $type,
+            'path' => 'set_zone_source/source_info_data_' . $table,
+            'row' => array
+            (
+                'id' => $id,
+            ),
+            'param' => array
+            (
+                'info_data_id' => $data_id,
+            ),
+        );
+        return $data;
+    }
+
+    public function getViewValue($data_id, &$content, &$button, &$media)
+    {
+        $button[] = array
+        (
+            'name' => '文件上传',
+            'type' => 'fastadd',
+            'path' => 'set_zone_source/source_info_data_value',
+            'row' => array
+            (
+                'id' => 0,
+            ),
+            'param' => array
+            (
+                'info_data_id' => $data_id,
+                'field' => 'info_data_id,value,content,name,sort,video',
+            ),
+        );
+
+        $tab = array();
+        $data_value_where['info_data_id'] = $data_id;
+        $data = Dever::db('info_data_value', 'source')->select($data_value_where);
+        if ($data) {
+            $media = true;
+            $link = Dever::url('source/video.view', array('type' => 'info_data_value'), true);
+            foreach ($data as &$v) {
+                
+            }
+
+            $tab = array
+            (
+                'name' => '所有文件',
+                'type' => 'table',
+                'border' => true,
+                //'height' => '200',
+                'head' => array
+                (
+                    array
+                    (
+                        'key' => 'sort',
+                        'name' => '排序',
+                        'fixed' => 'fixed',
+                    ),
+                    array
+                    (
+                        'key' => 'name',
+                        'name' => '名称',
+                        'fixed' => 'fixed',
+                    ),
+                    array
+                    (
+                        'key' => 'content',
+                        'name' => '内容',
+                        'fixed' => 'fixed',
+                    ),
+                ),
+                'button' => array
+                (
+                    array
+                    (
+                        'name' => '详情',
+                        'type' => 'link',
+                        'param' => $link,
+                    ),
+                    array
+                    (
+                        'name' => '编辑',
+                        'type' => 'fastedit',
+                        'path' => 'set_zone_source/source_info_data_value',
+                        'param' => 'dever_info',
+                    ),
+                    array
+                    (
+                        'name' => '视频处理',
+                        'type' => 'fastedit',
+                        'path' => 'set_zone_source/source_info_data_value',
+                        'param' => 'dever_video',
+                    ),
+                    array
+                    (
+                        'name' => '删除',
+                        'type' => 'api',
+                        'param' => Dever::url('source/data.delValue', array('info_data_id' => $data_id), true),
+                    ),
+                ),
+                'body' => $data,
+            );
+        }
+
+        
+        return $tab;
+    }
+
+    # 获取文案
+    private function getViewText($id, $data_id, &$content, &$button)
+    {
+        $db = Dever::db('info_data_content', 'source');
+        $edit = 'fastadd';
+        $text = '';
+        if ($id) {
+            $edit = 'fastedit';
+            $info = $db->find($id);
+            $text = '版本:' . $info['name'] ? $info['name'] : date('YmdHis', $info['cdate']);
+            $text .= '<br />状态:' . $db->value('status', $info);
+            if ($info['status'] == 2) {
+                $text .= '<br />文案:生成中......<a href="javascript:;" onclick="location.reload()">[点此刷新]</a>';
+            } else {
+                $text .= '<br />文案:' . htmlspecialchars_decode($info['content']);
+            }
+            # 后续改成流式获取
+            //$text = Dever::url('source/data.content', array('id' => $id));
+        } else {
+            $text = '暂无文案';
+        }
+
+        $content[] = array
+        (
+            'name' => '当前文案',
+            'type' => 'text',
+            'content' => $text,
+            'style' => 'primary',
+        );
+
+        $button[] = array
+        (
+            'name' => '智能文案',
+            'type' => $edit,
+            'path' => 'set_zone_source/source_info_data_content',
+            'row' => array
+            (
+                'id' => $id,
+            ),
+            'param' => array
+            (
+                'field' => 'name,prompt,info_data_id,ai_platform,ai_prompt_type,ai_prompt',
+                'info_data_id' => $data_id,
+            ),
+        );
+        /*
+        $button[] = array
+        (
+            'name' => '打开智能对话',
+            'type' => 'link',
+            'path' => 'platform/role',
+            'row' => array
+            (
+                'id' => $data_id,
+            ),
+        );
+        */
+        $button[] = array
+        (
+            'name' => '手动修改文案',
+            'type' => $edit,
+            'path' => 'set_zone_source/source_info_data_content',
+            'row' => array
+            (
+                'id' => $id,
+            ),
+            'param' => array
+            (
+                'field' => 'name,content,info_data_id',
+                'info_data_id' => $data_id,
+            ),
+        );
+
+        $version = Dever::db('info_data_content', 'source')->select(array('info_data_id' => $data_id));
+        if ($version) {
+            foreach ($version as &$v) {
+                $v['version'] = $v['name'] ? $v['name'] : date('YmdHis', $v['cdate']);
+                if ($id == $v['id']) {
+                    $v['version'] .= '[当前版本]';
+                }
+                $v['cdate'] = date('Y-m-d H:i:s', $v['cdate']);
+                $v['status'] = $db->value('status', $v);
+            }
+        }
+
+        $tab = array
+        (
+            'name' => '所有文案',
+            'type' => 'table',
+            'border' => true,
+            //'height' => '200',
+            'head' => array
+            (
+                array
+                (
+                    'key' => 'version',
+                    'name' => '版本',
+                    'fixed' => 'fixed',
+                ),
+                array
+                (
+                    'key' => 'status',
+                    'name' => '状态',
+                    'fixed' => 'fixed',
+                ),
+                array
+                (
+                    'key' => 'cdate',
+                    'name' => '时间',
+                    'fixed' => 'fixed',
+                ),
+            ),
+            'button' => array
+            (
+                array
+                (
+                    'name' => '详情',
+                    'type' => 'link',
+                    'param' => 'https://www.baidu.com/?info_data_id='.$data_id,
+                ),
+                array
+                (
+                    'name' => '切换',
+                    'type' => 'api',
+                    'param' => Dever::url('source/data.set', array('info_data_id' => $data_id)),
+                ),
+            ),
+            'body' => $version,
+        );
+        return $tab;
+    }
+
+    private function getViewLog($data_id)
+    {
+        return array
+        (
+            'name' => '操作日志',
+            'type' => 'table',
+            'border' => true,
+            //'height' => '500',
+            'head' => array
+            (
+                array
+                (
+                    'key' => 'username',
+                    'name' => '操作人',
+                    'fixed' => 'fixed',
+                ),
+                array
+                (
+                    'key' => 'cdate',
+                    'name' => '操作时间',
+                    'fixed' => 'fixed',
+                ),
+                array
+                (
+                    'key' => 'info',
+                    'name' => '操作内容',
+                    'fixed' => 'fixed',
+                ),
+            ),
+            'button' => array
+            (
+                
+            ),
+            'body' => Dever::load('log', 'source')->getList($data_id),
+        );
+    }
+}

+ 30 - 0
src/source/lib/Datavalue.php

@@ -0,0 +1,30 @@
+<?php namespace Source\Lib;
+use Dever;
+class Datavalue
+{
+    public function updateStart($data)
+    {
+        $host = Dever::host();
+        if (isset($data['value']) && $data['value']) {
+            if (!strstr($data['value'], $host)) {
+                $result = Dever::load('video', 'api')->remote($data['value'], false, true);
+                if (isset($result['vid'])) {
+                    $data['remote'] = $result['value'];
+                    $data['value'] = $result['video']['url'];
+                    $data['name'] = $result['title'] ? $result['title'] : $result['desc'];
+                    $data['vid'] = $result['vid'];
+                }
+            }
+        }
+        return $data;
+    }
+
+    public function updateEnd($data)
+    {
+        if (isset($data['video']) && $data['video'] == 1) {
+            $info = Dever::db('info_data_value')->find($data['id']);
+            Dever::load('video', 'source')->update($info, 3);
+        }
+        return $data;
+    }
+}

+ 14 - 352
src/source/lib/Info.php

@@ -2,362 +2,24 @@
 use Dever;
 class Info
 {
-    # 获取附件列表
-    public function getValueList($info_id)
+    public function update($data)
     {
-        $where['info_id'] = $info_id;
-        $data = Dever::db('info_value', 'source')->select($where);
-        if ($data) {
-            foreach ($data as &$v) {
-                if (!$v['name']) {
-                    $v['name'] = $v['file'];
+        $host = Dever::host();
+        $value = Dever::db('info_value', 'source')->select(array('info_id' => $data['id']));
+        if ($value) {
+            foreach ($value as $k => $v) {
+                if (!strstr($v['value'], $host)) {
+                    $data = Dever::load('video', 'api')->remote($v['value'], false, true);
+                    if (isset($data['vid'])) {
+                        $update['remote'] = $data['value'];
+                        $update['value'] = $data['video']['url'];
+                        $update['name'] = $data['title'] ? $data['title'] : $data['desc'];
+                        $update['vid'] = $data['vid'];
+                        Dever::db('info_value', 'source')->update(array('id' => $v['id']), $update);
+                    }
                 }
-                if (!$v['name']) {
-                    $v['name'] = $v['id'];
-                }
-            }
-        }
-        return $data;
-    }
-
-    # 获取素材详情页
-    public function getDataView($page)
-    {
-        # 这里获取基本信息
-        $db = Dever::db('info_data', 'source');
-        $info_data_content_db = Dever::db('info_data_content', 'source');
-        //print_r($page->info);die;
-        $relation_id = Dever::call("source/info.getValueList", array($page->info['info_id']));
-        $edit = 'fastadd';
-        $content = '';
-        if ($page->info['info_data_content_id']) {
-            $edit = 'fastedit';
-            $info_data_content = $info_data_content_db->find($page->info['info_data_content_id']);
-            $content = '版本:' . date('YmdHis', $info_data_content['cdate']);
-            $content .= '<br /><br />状态:' . $info_data_content_db->value('status', $info_data_content);
-            if ($page->info['type'] != 1) {
-                if ($info_data_content['file']) {
-                    $info_data_content['file'] = '<a href="_target" href="'.$info_data_content['file'].'">'.$info_data_content['file'].'</a>';
-                }
-                $content .= '<br /><br />文件:' . $info_data_content['file'];
-            }
-            if ($info_data_content['status'] == 2) {
-                $content .= '<br /><br />内容:生成中......<a href="javascript:;" onclick="fetchData()">[点此刷新]</a>';
-            } else {
-                $content .= '<br /><br />内容:' . htmlspecialchars_decode($info_data_content['content']);
-            }
-
-            # 后续改成流式获取
-            //$content = Dever::url('source/data.content', array('id' => $page->info['info_data_content_id']));
-        } else {
-            $content = '暂无内容';
-        }
-        $info[] = array
-        (
-            # 类型,desc描述 table表格,表格有head和body即可
-            'type' => 'desc',
-            'name' => $page->info['name'],
-            # 每行展示数量
-            'column' => 1,
-            # 是否有边框
-            'border' => true,
-            # 排列方向:horizontal横向 vertical纵向
-            'direction' => 'horizontal',
-            # 右侧按钮
-            'button' => array
-            (
-                array
-                (
-                    'name' => '修改素材配置',
-                    # fastedit、fastadd、oper、api、link、route
-                    'type' => 'fastedit',
-                    'path' => 'set_zone_source/source_info_data',
-                    'row' => array
-                    (
-                        'id' => $page->info['id'],
-                    ),
-                    'param' => array
-                    (
-                        'info_id' => $page->info['info_id'],
-                    ),
-                ),
-                array
-                (
-                    'name' => '智能生成内容',
-                    'type' => $edit,
-                    'path' => 'set_zone_source/source_info_data_content',
-                    'row' => array
-                    (
-                        'id' => $page->info['info_data_content_id'],
-                    ),
-                    'param' => array
-                    (
-                        'field' => 'prompt,info_data_id,ai_platform,ai_prompt_type,ai_prompt',
-                        'info_data_id' => $page->info['id'],
-                    ),
-                ),
-                /*
-                array
-                (
-                    'name' => '打开智能对话',
-                    'type' => 'link',
-                    'path' => 'platform/role',
-                    'row' => array
-                    (
-                        'id' => $page->info['id'],
-                    ),
-                ),*/
-                array
-                (
-                    'name' => '手动修改内容',
-                    'type' => $edit,
-                    'path' => 'set_zone_source/source_info_data_content',
-                    'row' => array
-                    (
-                        'id' => $page->info['info_data_content_id'],
-                    ),
-                    'param' => array
-                    (
-                        'field' => 'content,file,info_data_id',
-                        'info_data_id' => $page->info['id'],
-                    ),
-                ),
-                array
-                (
-                    'name' => '分发设置',
-                    'type' => 'fastedit',
-                    'path' => 'platform/role',
-                    'row' => array
-                    (
-                        'id' => $page->info['id'],
-                    ),
-                ),
-            ),
-            # 具体内容
-            'content' => array
-            (
-                array
-                (
-                    'name' => '素材类型',
-                    # 类型,text普通文本,tag标签,link链接,image图片 progress进度条 stat统计 timeline时间线 table表格
-                    'type' => 'text',
-                    'content' => $db->value('type', $page->info),
-                    # 样式primary success warning danger info exception
-                    'style' => 'primary',
-                ),
-                array
-                (
-                    'name' => '读取资源',
-                    'type' => 'text',
-                    'content' => $db->value('relation', $page->info),
-                    'style' => 'primary',
-                ),
-                array
-                (
-                    'name' => '资源附件',
-                    'type' => 'text',
-                    'content' => $page->info['relation'] == 2 ? Dever::in_array($relation_id, $page->info['relation_id']) : '-',
-                    'style' => 'primary',
-                ),
-            ),
-        );
-
-        $log = Dever::load('log', 'source')->getList($page->info['id']);
-        $history = Dever::db('info_data_content', 'source')->select(array('info_data_id' => $page->info['id']));
-        if ($history) {
-            foreach ($history as &$v) {
-                $v['version'] = date('YmdHis', $v['cdate']);
-                if ($page->info['info_data_content_id'] == $v['id']) {
-                    $v['version'] .= '[当前版本]';
-                }
-                $v['cdate'] = date('Y-m-d H:i:s', $v['cdate']);
-                $v['status'] = $info_data_content_db->value('status', $v);
-            }
-        }
-        $tab = array
-        (
-            'active' => 'tab1',
-            'content' => array
-            (
-                'tab1' => array
-                (
-                    'name' => '当前版本',
-                    'type' => 'text',
-                    'content' => $content,
-                    'style' => 'primary',
-                ),
-
-                'tab2' => array
-                (
-                    'name' => '所有版本',
-                    'type' => 'table',
-                    'border' => true,
-                    //'height' => '200',
-                    'head' => array
-                    (
-                        array
-                        (
-                            'key' => 'version',
-                            'name' => '版本',
-                            'fixed' => 'fixed',
-                        ),
-                        array
-                        (
-                            'key' => 'status',
-                            'name' => '状态',
-                            'fixed' => 'fixed',
-                        ),
-                        array
-                        (
-                            'key' => 'cdate',
-                            'name' => '时间',
-                            'fixed' => 'fixed',
-                        ),
-                    ),
-                    'button' => array
-                    (
-                        array
-                        (
-                            'name' => '详情',
-                            'type' => 'link',
-                            'param' => 'https://www.baidu.com/?info_data_id='.$page->info['id'],
-                        ),
-                        array
-                        (
-                            'name' => '切换',
-                            'type' => 'api',
-                            'param' => Dever::url('source/data.set', array('info_data_id' => $page->info['id'])),
-                        ),
-                    ),
-                    'body' => $history,
-                ),
-
-                /*
-                'tab3' => array
-                (
-                    'name' => '统计信息',
-                    'type' => 'text',
-                    'content' => '内容',
-                    'style' => 'primary',
-                ),*/
-
-                'tab4' => array
-                (
-                    'name' => '操作日志',
-                    'type' => 'table',
-                    'border' => true,
-                    //'height' => '500',
-                    'head' => array
-                    (
-                        array
-                        (
-                            'key' => 'username',
-                            'name' => '操作人',
-                            'fixed' => 'fixed',
-                        ),
-                        array
-                        (
-                            'key' => 'cdate',
-                            'name' => '操作时间',
-                            'fixed' => 'fixed',
-                        ),
-                        array
-                        (
-                            'key' => 'info',
-                            'name' => '操作内容',
-                            'fixed' => 'fixed',
-                        ),
-                    ),
-                    'button' => array
-                    (
-                        
-                    ),
-                    'body' => $log,
-                ),
-            )
-        );
-        return array('info' => $info, 'tab' => $tab);
-    }
-
-    public function updateDataStart($data)
-    {
-        if (isset($data['content_version']) && $data['content_version']) {
-            if ($data['content_version'] <= 0) {
-                $data['info_data_content_id'] = 0;
-            } else {
-                $data['info_data_content_id'] = $data['content_version'];
-            }
-            unset($data['content_version']);
-        }
-        return $data;
-    }
-
-    public function updateData($data)
-    {
-        $db = Dever::db('info_data', 'source');
-        $relation_id = Dever::call("source/info.getValueList", array($data['info_id']));
-        $info = '更新素材配置:' . $data['name'] . ' / 素材类型:' . $db->value('type', $data) . ' / 读取资源:' . $db->value('relation', $data) . ' / 资源附件:' . ($data['relation'] == 2 ? Dever::in_array($relation_id, $data['relation_id']) : '-');
-        if (isset($data['info_data_content_id'])) {
-            if ($data['info_data_content_id'] > 0) {
-                $content = Dever::db('info_data_content', 'source')->find($data['info_data_content_id']);
-                $info .= ' / 内容版本:' . date('YmdHis', $content['cdate']);
-            } else {
-                $info .= ' / 内容版本:新增内容';
-            }
-        }
-        Dever::load('log', 'source')->add($data['id'], $info);
-        return $data;
-    }
-
-    public function updateDataContent($data)
-    {
-        Dever::db('info_data', 'source')->update(array('id' => $data['info_data_id']), array('info_data_content_id' => $data['id']));
-        if (isset($data['ai_platform'])) {
-            $info = '更新智能生成';
-            if ($data['ai_platform']) {
-                if (is_string($data['ai_platform'])) {
-                    $data['ai_platform'] = explode(',', $data['ai_platform']);
-                }
-                $platform = Dever::db('platform', 'ai')->find($data['ai_platform'][0]);
-                if ($data['ai_platform'][1] == 2) {
-                    $platform_type = '智能体';
-                    $platform_child = Dever::db('agents', 'ai')->find($data['ai_platform'][2]);
-                } else {
-                    $platform_type = '模型';
-                    $platform_child = Dever::db('model', 'ai')->find($data['ai_platform'][2]);
-                }
-                $info .= ',智能平台:' . $platform['name'] . '-' . $platform_type . '-' . $platform_child['name'];
-            }
-            $info .= ' / 提示词类型:' . ($data['ai_prompt_type'] == 1 ? '通用提示词' : '自定义');
-            if ($data['ai_prompt_type'] == 1) {
-                if (is_string($data['ai_prompt'])) {
-                    $data['ai_prompt'] = explode(',', $data['ai_prompt']);
-                }
-                $prompt = Dever::db('prompt', 'ai')->find($data['ai_prompt'][1]);
-                $info .= ' / 提示词:' . $prompt['name'];
-            } else {
-                $info .= ' / 提示词:' . $data['prompt'];
-            }
-            
-            Dever::load('log', 'source')->add($data['info_data_id'], $info);
-            # 更改当前素材状态
-            Dever::db('info_data_content', 'source')->update(array('id' => $data['id']), array('status' => 2));
-            # 提交生成任务
-            \Dever\Helper\Cmd::run('task.create', array('id' => $data['id'], 'authorization' => \Dever\Helper\Str::encode(\Dever\Helper\Env::header('authorization'))), 'source');
-        }
-        if (isset($data['file'])) {
-            $info = '手动修改内容';
-            if ($data['file']) {
-                $info = ',更新文件:' . $data['file'];
-            }
-            if ($data['content']) {
-                $info = ',更新内容';
             }
-            Dever::load('log', 'source')->add($data['info_data_id'], $info);
-            # 更改当前素材状态
-            Dever::db('info_data_content', 'source')->update(array('id' => $data['id']), array('status' => 3));
         }
-        
         return $data;
     }
 }

+ 27 - 0
src/source/lib/Value.php

@@ -0,0 +1,27 @@
+<?php namespace Source\Lib;
+use Dever;
+class Value
+{
+    # 获取附件列表
+    public function getList($info_id)
+    {
+        $data[0] = array
+        (
+            'id' => -1,
+            'name' => '全部附件',
+        );
+        $where['info_id'] = $info_id;
+        $data = array_merge($data, Dever::db('info_value', 'source')->select($where));
+        if ($data) {
+            foreach ($data as &$v) {
+                if (!$v['name']) {
+                    $v['name'] = $v['value'];
+                }
+                if (!$v['name']) {
+                    $v['name'] = $v['id'];
+                }
+            }
+        }
+        return $data;
+    }
+}

+ 216 - 0
src/source/lib/Video.php

@@ -0,0 +1,216 @@
+<?php namespace Source\Lib;
+use Dever;
+class Video
+{
+    public function update($data, $time = false)
+    {
+        $getInfo = false;
+        if (isset($data['vid'])) {
+            $table = 'info_data_value';
+            $info_data_value = Dever::db('info_data_value', 'source')->find($data['id']);
+
+            if (!$info_data_value) {
+                Dever::error('附件不存在');
+            }
+            if (!$info_data_value['value'] && !$info_data_value['vid']) {
+                Dever::error('附件不存在');
+            }
+            if ($info_data_value['vid']) {
+                $video = Dever::db('video', 'api')->find($info_data_value['vid']);
+                if (!$video) {
+                    Dever::error('视频不存在');
+                }
+            } else {
+                $video = $info_data_value;
+                $video['author'] = '';
+                $video['file'] = $info_data_value['value'];
+                $video['pdate'] = $info_data_value['cdate'];
+                $video['name'] = $info_data_value['name'];
+            }
+            $info_data = Dever::db('info_data', 'source')->find($info_data_value['info_data_id']);
+            $getInfo = true;
+            
+        } elseif (isset($data['info_data_id'])) {
+            $info_data = Dever::db('info_data', 'source')->find($data['info_data_id']);
+            $table = 'info_data_video';
+            if ($info_data['type'] == 3) {
+                if ($info_data['relation'] == 1) {
+                    Dever::error('读取资源为内容,视频处理功能暂时不支持,请更换为附件');
+                }
+                if (!$info_data['relation_id']) {
+                    Dever::error('请先选择附件');
+                }
+                $info_value = Dever::db('info_value', 'source')->find($info_data['relation_id']);
+                if (!$info_value) {
+                    Dever::error('附件不存在');
+                }
+                if (!$info_value['value'] && !$info_value['vid']) {
+                    Dever::error('附件不存在');
+                }
+                if ($info_value['vid']) {
+                    $video = Dever::db('video', 'api')->find($info_value['vid']);
+                    if (!$video) {
+                        Dever::error('视频不存在');
+                    }
+                } else {
+                    $video = $info_value;
+                    $video['author'] = '';
+                    $video['file'] = $info_value['value'];
+                    $video['pdate'] = $info_value['cdate'];
+                    $video['name'] = $info_value['name'];
+                }
+                $getInfo = true;
+            } elseif ($info_data['type'] == 10) {
+                $video = Dever::db('info_data_collection', 'source')->find(array('info_data_id' => $data['info_data_id']));
+                if (!$video) {
+                    Dever::error('合集不存在');
+                }
+                if (!$video['file']) {
+                    Dever::error('合集视频不存在');
+                }
+                $video['pdate'] = $video['cdate'];
+                $video['name'] = $info_data['name'];
+            }
+        }
+
+        if ($data['type'] == 1 && $data['template_id']) {
+            $template = Dever::db('video_template', 'api')->find($data['template_id']);
+        } elseif ($data['type'] == 2 && $data['template_id']) {
+            $template = Dever::db('video_template', 'api')->find($data['template_id']);
+        } else {
+            $template = array();
+        }
+        if ($template) {
+            $url = $video['file'];
+            $local = Dever::load('view', 'upload')->local($url);
+            if (!is_file($local)) {
+                Dever::error('视频不存在');
+            }
+            $option = array();
+
+            # 获取视频高度和宽度
+            if ($getInfo) {
+                $video_info = Dever::load('video', 'api')->info($local);
+                $seconds = $video_info['seconds'];
+                if ($time) {
+                    if ($seconds >= $time*2) {
+                        $data['time_length'] = $time;
+                        $data['time_start'] = $time;
+                    } elseif ($seconds >= $time) {
+                        $data['time_length'] = $time;
+                        $data['time_start'] = 0;
+                    } else {
+                        $data['time_length'] = $time-1;
+                        $data['time_start'] = 0;
+                    }
+                }
+                if ($video_info['width'] > $video_info['height']) {
+                    $option['video']['scale'] = '1920:1080';
+                    $split = 'split[a][b];[a]scale=1920:1080,boxblur=20:5[1];[b]scale=1920/1.008:-1[2];[1][2]overlay=(W-w)/2:(H-h)/2';
+                } else {
+                    $option['video']['scale'] = '1080:1920';
+                    $split = 'split[a][b];[a]scale=1080:1920,boxblur=20:5[1];[b]scale=1080/1.008:-1[2];[1][2]overlay=(W-w)/2:(H-h)/2';
+                }
+            }
+
+            if ($template['flip'] == 1) {
+                $option['video']['flip'] = 1;
+            }
+            if ($template['setpts'] > 0) {
+                $option['video']['setpts'] = $template['setpts'];
+            }
+
+            # 降噪
+            //$option['video']['mp'] = 'denoise3d';
+            $option['video']['hqdn3d'] = '';
+            # 锐化
+            $option['video']['unsharp'] = '';
+            # 模糊
+            //$option['video']['smartblur'] = '5:0.8:0';
+
+            if ($data['text']) {
+                $text_type = 'text';
+                if ($data['text_type'] == 2) {
+                    $text_type = 'textmove';
+                }
+                $admin = Dever::load('common', 'manage')->info();
+                $video['admin'] = $admin['name'];
+                if (!isset($video['author'])) {
+                    $video['author'] = $video['admin'];
+                }
+                $video['data_name'] = $info_data['name'];
+                $extend = Dever::load('common', 'manage')->extend();
+                $zone = Dever::db('zone', 'source')->find($extend['data_id']);
+                if ($zone) {
+                    $video['zone_name'] = $zone['name'];
+                } else {
+                    $video['zone_name'] = '';
+                }
+                $source = Dever::db('info', 'source')->find($info_data['info_id']);
+                $video['source_name'] = $source['name'];
+                if ($video['author']) {
+                    $option['video'][$text_type][] = \Dever\Helper\Str::val($data['text'], $video);
+                    $option['video'][$text_type][] = $data['text_size'];
+                    $option['video'][$text_type][] = $data['text_color'];
+                    $option['video'][$text_type][] = $data['text_alpha'];
+                    if ($data['text_type'] == 2) {
+                        $option['video'][$text_type][] = $data['text_move'];
+                    } else {
+                        $option['video'][$text_type][] = $data['text_x'];
+                        $option['video'][$text_type][] = $data['text_y'];
+                    }
+                }
+            }
+
+            # 对视频进行四周模糊处理
+            //split[a][b];[a]scale=1080:1920,boxblur=20:5[1];[b]scale=1080/1.015:1920/1.012[2];[1][2]overlay=(W-w)/2:(H-h)/2
+            if (isset($split)) {
+                $option['video'][$split] = '';
+            }
+            # hue:调整视频色调、饱和度、亮度
+            $option['video']['hue'] = 's=1:b=0.5:h=0.5';
+            # 颜色平衡
+            //$option['video']['colorbalance'] = 'rh=.3:gh=.3:bh=.3';
+            
+            if ($data['time_length'] > 0) {
+                $option['time'] = array($data['time_length'], $data['time_start']);
+            }
+            /*
+            $option['filter_complex'] = array
+            (
+                '[1:v]chromakey=0x00d810:0.1:0.2[ckout]',
+                '[0:v][ckout]overlay[out]',
+            );*/
+            $output = array
+            (
+                'file' => $local . '_clip.mp4',
+                'option' => array
+                (
+                    # 覆盖
+                    'y' => '',
+                    # 音频码率
+                    //'b:a' => '10000k',
+                    # 视频码率
+                    //'b:v' => '10000k',
+                    'r' => '25',
+                    'c:v' => 'libx264',
+                    'preset' => 'ultrafast',//ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo。
+                    'movflags' => 'faststart', 
+                    'pix_fmt' => 'yuv420p',
+                    'crf' => '18',
+                    'threads' => '16',
+                    'nr' => '500',
+                    'an' => '',
+                    //'sn' => '',
+                    //'vn' => '',
+                    'metadata' => 'md5=' . md5($video['id']),
+                ),
+            );
+            Dever::load('video', 'api')->handle($local, $output, $option);
+
+            Dever::db($table, 'source')->update(array('id' => $data['id']), array('file' => $url . '_clip.mp4'));
+        }
+        
+        return $data;
+    }
+}

+ 8 - 6
src/source/table/info_data.php

@@ -26,21 +26,23 @@ return array
             'value'     => array
             (
                 1 => '文章',
-                2 => '图片',
+                //2 => '图片',
                 3 => '短视频',
-                4 => '音频',
+                //4 => '音频',
+                10 => '视频合集',
             ),
         ),
 
         'relation' => array
         (
-            'name'      => '读取资源',
+            'name'      => '素材内容',
             'type'      => 'tinyint(1)',
             'default'   => 1,
             'value'     => array
             (
-                1 => '内容',
-                2 => '附件',
+                1 => '读取资源的内容',
+                2 => '读取资源的附件',
+                3 => '手动上传',
             ),
         ),
 
@@ -52,7 +54,7 @@ return array
 
         'info_data_content_id' => array
         (
-            'name'      => '素材内容',
+            'name'      => '素材文案',
             'type'      => 'int(11)',
         ),
 

+ 53 - 0
src/source/table/info_data_collection.php

@@ -0,0 +1,53 @@
+<?php
+return array
+(
+    'name' => '资源推广素材合集',
+    'partition' => 'Dever::call("manage/common.system")',
+    'struct' => array
+    (
+        'info_data_id' => array
+        (
+            'name'      => '资源素材ID',
+            'type'      => 'int(11)',
+        ),
+
+        'type' => array
+        (
+            'name'      => '素材来源',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '读取所有短视频素材',
+                //2 => '选择短视频素材',
+                //3 => '自定义',
+            ),
+        ),
+
+        'template' => array
+        (
+            'name'      => '绿幕模板',
+            'type'      => 'varchar(200)',
+        ),
+
+        'file' => array
+        (
+            'name'      => '生成文件',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'status' => array
+        (
+            'name'      => '状态',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '待定',
+                2 => '进行中',
+                3 => '已完成',
+                4 => '失败',
+            ),
+        ),
+    ),
+);

+ 7 - 7
src/source/table/info_data_content.php

@@ -11,6 +11,12 @@ return array
             'type'      => 'int(11)',
         ),
 
+        'name' => array
+        (
+            'name'      => '文案名称',
+            'type'      => 'varchar(200)',
+        ),
+
         'ai_platform' => array
         (
             'name'      => '智能平台',
@@ -43,16 +49,10 @@ return array
 
         'content'       => array
         (
-            'name'      => '推广内容',
+            'name'      => '推广文案',
             'type'      => 'text',
         ),
 
-        'file' => array
-        (
-            'name'      => '推广文件',
-            'type'      => 'varchar(2000)',
-        ),
-
         'status' => array
         (
             'name'      => '状态',

+ 86 - 0
src/source/table/info_data_value.php

@@ -0,0 +1,86 @@
+<?php
+$video = include('info_data_video.php');
+$config = array
+(
+    'name' => '资源素材的附件',
+    'partition' => 'Dever::call("manage/common.system")',
+    'order' => 'sort asc,id asc',
+    'struct' => array
+    (
+        'info_data_id' => array
+        (
+            'name'      => '资源素材ID',
+            'type'      => 'int(11)',
+        ),
+
+        'name' => array
+        (
+            'name'      => '名称',
+            'type'      => 'varchar(200)',
+        ),
+
+        'vid' => array
+        (
+            'name'      => '文件资源id',
+            'type'      => 'int(11)',
+        ),
+
+        'value' => array
+        (
+            'name'      => '文件',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'file' => array
+        (
+            'name'      => '生成文件',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'remote' => array
+        (
+            'name'      => '远程地址',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'content' => array
+        (
+            'name'      => '内容',
+            'type'      => 'varchar(8000)',
+        ),
+
+        'sort' => array
+        (
+            'name'      => '排序',
+            'type'      => 'int(11)',
+            'default'   => '1',
+        ),
+
+        'video' => array
+        (
+            'name'      => '视频处理',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '是',
+                2 => '否',
+            ),
+        ),
+
+        'status' => array
+        (
+            'name'      => '状态',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '展示',
+                2 => '不展示',
+            ),
+        ),
+    ),
+);
+
+$config['struct'] = array_merge($config['struct'], $video['struct']);
+return $config;

+ 129 - 0
src/source/table/info_data_video.php

@@ -0,0 +1,129 @@
+<?php
+return array
+(
+    'name' => '资源推广素材视频',
+    'partition' => 'Dever::call("manage/common.system")',
+    'struct' => array
+    (
+        'info_data_id' => array
+        (
+            'name'      => '资源素材ID',
+            'type'      => 'int(11)',
+        ),
+
+        'type' => array
+        (
+            'name'      => '处理类型',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '官方模板',
+                //2 => '自用模板',
+                //3 => '自定义',
+            ),
+        ),
+
+        'template_id' => array
+        (
+            'name'      => '处理模板',
+            'type'      => 'int(11)',
+            'default'   => 1,
+        ),
+
+        'file' => array
+        (
+            'name'      => '生成文件',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'time_length' => array
+        (
+            'name'      => '截取时间长度',
+            'type'      => 'varchar(10)',
+            'default'   => '0',
+        ),
+
+        'time_start' => array
+        (
+            'name'      => '截取开始时间',
+            'type'      => 'varchar(10)',
+            'default'   => '0',
+        ),
+
+        'text' => array
+        (
+            'name'      => '水印文字',
+            'type'      => 'varchar(200)',
+            'default'   => "@{author}",
+        ),
+
+        'text_type' => array
+        (
+            'name'      => '文字类型',
+            'type'      => 'tinyint(1)',
+            'default'   => '1',
+            'value'     => array
+            (
+                1 => '普通文字',
+                2 => '对角移动',
+            ),
+        ),
+
+        'text_size' => array
+        (
+            'name'      => '文字大小',
+            'type'      => 'int(11)',
+            'default'   => '25',
+        ),
+
+        'text_color' => array
+        (
+            'name'      => '文字颜色',
+            'type'      => 'varchar(10)',
+            'default'   => 'white',
+        ),
+
+        'text_alpha' => array
+        (
+            'name'      => '透明度',
+            'type'      => 'varchar(5)',
+            'default'   => '0.3',
+        ),
+
+        'text_x' => array
+        (
+            'name'      => 'x坐标位置',
+            'type'      => 'varchar(25)',
+            'default'   => '50',
+        ),
+
+        'text_y' => array
+        (
+            'name'      => 'y坐标位置',
+            'type'      => 'varchar(25)',
+            'default'   => '50',
+        ),
+
+        'text_move' => array
+        (
+            'name'      => '移动速度',
+            'type'      => 'varchar(25)',
+            'default'   => '200',
+        ),
+
+        'status' => array
+        (
+            'name'      => '状态',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '待定',
+                2 => '进行中',
+                3 => '已完成',
+                4 => '失败',
+            ),
+        ),
+    ),
+);

+ 12 - 0
src/source/table/info_value.php

@@ -18,12 +18,24 @@ return array
             'type'      => 'varchar(200)',
         ),
 
+        'vid' => array
+        (
+            'name'      => '文件资源id',
+            'type'      => 'int(11)',
+        ),
+
         'value' => array
         (
             'name'      => '文件',
             'type'      => 'varchar(2000)',
         ),
 
+        'remote' => array
+        (
+            'name'      => '远程地址',
+            'type'      => 'varchar(2000)',
+        ),
+
         'content' => array
         (
             'name'      => '内容',

+ 29 - 1
src/source/table/manage/core.php

@@ -88,6 +88,33 @@ return array
             'show'      => 3,
         ),
 
+        'source_info_data_video' => array
+        (
+            'parent'    => 'set_zone_source',
+            'name'      => '推广素材视频处理',
+            'icon'      => 'album-line',
+            'sort'      => '2',
+            'show'      => 3,
+        ),
+
+        'source_info_data_value' => array
+        (
+            'parent'    => 'set_zone_source',
+            'name'      => '推广素材附件',
+            'icon'      => 'album-line',
+            'sort'      => '2',
+            'show'      => 3,
+        ),
+
+        'source_info_data_collection' => array
+        (
+            'parent'    => 'set_zone_source',
+            'name'      => '推广素材合集处理',
+            'icon'      => 'album-line',
+            'sort'      => '2',
+            'show'      => 3,
+        ),
+
         'source_cate' => array
         (
             'parent'    => 'set_zone_source',
@@ -112,6 +139,7 @@ return array
             'sort'      => '5',
         ),
 
+        /*
         'set_zone_task' => array
         (
             'parent'    => 'set_zone',
@@ -142,6 +170,6 @@ return array
             'name'      => '素材分发',
             'icon'      => 'coupon-4-line',
             'sort'      => '3',
-        ),
+        ),*/
     ),
 );

+ 53 - 0
src/source/table/manage/info_data_file.php

@@ -0,0 +1,53 @@
+<?php
+return array
+(
+    'name' => '资源推广素材合集里的文件',
+    'partition' => 'Dever::call("manage/common.system")',
+    'struct' => array
+    (
+        'info_data_collection_id' => array
+        (
+            'name'      => '资源素材ID',
+            'type'      => 'int(11)',
+        ),
+
+        'type' => array
+        (
+            'name'      => '素材来源',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '读取所有短视频素材',
+                //2 => '选择短视频素材',
+                //3 => '自定义',
+            ),
+        ),
+
+        'template' => array
+        (
+            'name'      => '绿幕模板',
+            'type'      => 'varchar(200)',
+        ),
+
+        'file' => array
+        (
+            'name'      => '生成文件',
+            'type'      => 'varchar(2000)',
+        ),
+
+        'status' => array
+        (
+            'name'      => '状态',
+            'type'      => 'tinyint(1)',
+            'default'   => 1,
+            'value'     => array
+            (
+                1 => '待定',
+                2 => '进行中',
+                3 => '已完成',
+                4 => '失败',
+            ),
+        ),
+    ),
+);

+ 80 - 0
src/source/table/manage/info_data_video.php

@@ -0,0 +1,80 @@
+<?php
+return array
+(
+    'source' => $source,
+    'update' => array
+    (
+        'control' => array
+        (
+            'text_x' => array
+            (
+                'text_type' => 1,
+            ),
+            'text_y' => array
+            (
+                'text_type' => 1,
+            ),
+            'text_move' => array
+            (
+                'text_type' => 2,
+            ),
+        ),
+        'tab' => array
+        (
+            '基本处理' => $data_id.',type,template_id,time_length,time_start',
+            '文字处理' => 'text,text_type,text_size,text_color,text_alpha,text_x,text_y,text_move',
+            //'片头片尾' => '',
+        ),
+        //'desc' => '原创声明:',
+        'field'    => array
+        (
+            $data_id => array
+            (
+                'type' => 'hidden',
+            ),
+            'type' => array
+            (
+                'type' => 'radio',
+                'remote' => 'source/data.getVideoTemplate',
+            ),
+            'template_id' => array
+            (
+                'type' => 'select',
+            ),
+            
+            'time_length' => array
+            (
+                'desc' => '请输入秒数,为0则不做截取',
+                'default' => $check ? '3' : '0',
+            ),
+            'time_start' => array
+            (
+                'desc' => '请输入秒数',
+                'default' => $check ? '3' : '0',
+            ),
+            'text' => array
+            (
+                'desc' => '请输入要添加的文字,变量:{author}是视频作者名称,{pdate}是视频发布时间,{name}是视频的标题, {admin}是当前操作人名称,{data_name}是当前素材的名称,{source_name}是当前资源的名称,{zone_name}是当前源区的名称',
+            ),
+            'text_type' => array
+            (
+                'type' => 'radio',
+                'control' => true,
+            ),
+            'text_size' => array
+            (
+                'default' => $check ? '25' : '30',
+            ),
+            'text_color',
+            'text_alpha' => array
+            (
+                'default' => $check ? '1' : '0.3',
+            ),
+            'text_x',
+            'text_y',
+            'text_move',
+        ),
+        # update提交之后的操作
+        'end' => 'source/video.update',
+    ),
+);

+ 2 - 1
src/source/table/manage/info_value.php

@@ -10,8 +10,9 @@ return array
                 'name' => '文件',
                 'field' => 'value',
                 'type' => 'upload',
-                'upload' => 1,
+                'upload' => 8,
                 'yun' => true,
+                'limit' => 1,
                 'multiple' => false,
                 'tip' => '',
                 'style' => 'input',

+ 2 - 0
src/source/table/manage/source_info.php

@@ -91,8 +91,10 @@ return array
             'source/info_value' => array
             (
                 'name' => '附件',
+                'desc' => '如果需要抓取网络资源,直接在"文件"中填写网络资源的链接即可<br />点击保存后,系统将会自动抓取网络资源',
                 'where'  => array('info_id' => 'id'),
             ),
         ),
+        'end' => 'source/info.update',
     ),
 );

+ 10 - 8
src/source/table/manage/source_info_data.php

@@ -9,7 +9,8 @@ if (is_string($set)) {
 $info_id = $set['info_id'] ?? false;
 $relation = array
 (
-    1 => '内容',
+    1 => '读取资源内容',
+    3 => '手动上传',
 );
 $relation_id = array();
 if ($info_id) {
@@ -17,10 +18,11 @@ if ($info_id) {
     if ($info_value) {
         $relation = array
         (
-            1 => '内容',
-            2 => '附件',
+            1 => '读取资源内容',
+            2 => '读取资源附件',
+            3 => '手动上传',
         );
-        $relation_id = 'Dever::call("source/info.getValueList", array('.$info_id.'))';
+        $relation_id = 'Dever::call("source/value.getList", array('.$info_id.'))';
     }
 }
 
@@ -61,7 +63,7 @@ return array
                 //'tip' => false,
             ),
             'type',
-            'relation',
+            //'relation',
             'status' => array
             (
                 'type' => 'switch',
@@ -143,10 +145,10 @@ return array
             ),
         ),
         # update提交之后的操作
-        'start' => 'source/info.updateDataStart',
-        'end' => 'source/info.updateData',
+        'start' => 'source/data.updateStart',
+        'end' => 'source/data.update',
     ),
 
     # 详情页配置
-    'view' => 'source/info.getDataView',
+    'view' => 'source/data.getView',
 );

+ 31 - 0
src/source/table/manage/source_info_data_collection.php

@@ -0,0 +1,31 @@
+<?php
+return array
+(
+    'source' => 'source/info_data_collection',
+    'update' => array
+    (
+        'field'    => array
+        (
+            'info_data_id' => array
+            (
+                'type' => 'hidden',
+            ),
+            /*
+            'type' => array
+            (
+                'type' => 'radio',
+            ),*/
+            'template' => array
+            (
+                'type' => 'upload',
+                'upload' => 3,
+                'yun' => true,
+                'multiple' => false,
+                'tip' => '',
+                'style' => 'list',
+            ),
+        ),
+        # update提交之后的操作
+        'end' => 'source/collection.update',
+    ),
+);

+ 4 - 14
src/source/table/manage/source_info_data_content.php

@@ -44,6 +44,7 @@ return array
             (
                 'type' => 'hidden',
             ),
+            'name' => '',
             'ai_platform' => array
             (
                 'name' => '智能平台',
@@ -68,22 +69,11 @@ return array
                 //'rules' => true,
                 'type' => 'textarea',
                 'autosize' => array('minRows' => 5),
-                'desc' => '将根据素材配置以及提示词自动生成素材内容,生成进度请查看版本信息,提示词可以为空,点击确定将启动生成任务',
-            ),
-            'file' => array
-            (
-                'show' => $upload_show,
-                'name' => '文件',
-                'type' => 'upload',
-                'upload' => $upload,
-                'yun' => true,
-                'multiple' => false,
-                'tip' => '',
-                'style' => 'list',
+                'desc' => '将根据素材配置以及提示词自动生成素材的文案,生成进度请查看版本信息,提示词可以为空,点击确定将启动生成任务',
             ),
             'content' => array
             (
-                'name' => '内容',
+                'name' => '文案',
                 //'rules' => true,
                 'type' => 'editor',
                 'editorMenu' => array
@@ -94,6 +84,6 @@ return array
             ),
         ),
         # update提交之后的操作
-        'end' => 'source/info.updateDataContent',
+        'end' => 'source/content.update',
     ),
 );

+ 64 - 0
src/source/table/manage/source_info_data_value.php

@@ -0,0 +1,64 @@
+<?php
+$field = Dever::input('field');
+if ($field == 'dever_video') {
+    $check = true;
+    $data_id = 'info_data_id';
+    $source = 'source/info_data_value';
+    $video = include('info_data_video.php');
+    $update = $video['update'];
+    $update['field']['vid'] = '';
+
+} else {
+    
+    $update = array
+    (
+        'field'    => array
+        (
+            'info_data_id' => array
+            (
+                'type' => 'hidden',
+            ),
+            'value' => array
+            (
+                'name' => '文件',
+                'type' => 'upload',
+                'upload' => 8,
+                'yun' => true,
+                'limit' => 1,
+                'multiple' => false,
+                'tip' => '',
+                'style' => 'input',
+                'upload_name' => 'name',
+            ),
+            'name' => array
+            (
+                'name' => '名称',
+            ),
+            'content' => array
+            (
+                'name' => '内容',
+                'type' => 'textarea',
+            ),
+            'video' => array
+            (
+                'type' => 'radio',
+                'desc' => '保存后,将自动处理视频,如果对处理结果不满意,可以重新处理',
+            ),
+            'sort' => array
+            (
+                
+            ),
+        ),
+        'start' => 'source/datavalue.updateStart',
+        'end' => 'source/datavalue.updateEnd',
+    );
+
+    if ($field == 'dever_info') {
+        unset($update['field']['video']);
+    }
+}
+return array
+(
+    'source' => 'source/info_data_value',
+    'update' => $update,
+);

+ 18 - 0
src/source/table/manage/source_info_data_video.php

@@ -0,0 +1,18 @@
+<?php
+$set = Dever::input('set');
+if (!$set) {
+    $set = Dever::input('field');
+}
+if (is_string($set)) {
+    $set = Dever::json_decode(html_entity_decode($set));
+}
+$info_data_id = $set['info_data_id'] ?? false;
+$info_data = array();
+if ($info_data_id) {
+    $info_data = Dever::db('info_data', 'source')->find($info_data_id);
+}
+$check = $info_data && $info_data['type'] == 3;
+
+$data_id = 'info_data_id';
+$source = 'source/info_data_video';
+return include('info_data_video.php');

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů