李帅

1.新增字体管理。

...@@ -4,21 +4,21 @@ namespace App\Admin\Controllers; ...@@ -4,21 +4,21 @@ namespace App\Admin\Controllers;
4 4
5 use App\Admin\Renderable\PoemTable; 5 use App\Admin\Renderable\PoemTable;
6 use App\Admin\Renderable\TemplateTable; 6 use App\Admin\Renderable\TemplateTable;
7 -use App\Jobs\MakeImages; 7 +use App\Jobs\AdminMakeImmerse;
8 -use App\Jobs\MakeVideo; 8 +use App\Admin\Repositories\AdminMakeVideo;
9 -use App\Models\AdminMakeVideo;
10 use App\Models\Immerse; 9 use App\Models\Immerse;
11 -use App\Models\Order; 10 +use App\Models\OnePoem;
11 +use App\Models\VideoTemp;
12 use Dcat\Admin\Form; 12 use Dcat\Admin\Form;
13 use Dcat\Admin\Grid; 13 use Dcat\Admin\Grid;
14 -use Dcat\Admin\Layout\Content;
15 use Dcat\Admin\Show; 14 use Dcat\Admin\Show;
16 use Dcat\Admin\Http\Controllers\AdminController; 15 use Dcat\Admin\Http\Controllers\AdminController;
17 use Illuminate\Support\Facades\Storage; 16 use Illuminate\Support\Facades\Storage;
18 -use Illuminate\Support\Str;
19 17
20 class AdminMakeVideoController extends AdminController 18 class AdminMakeVideoController extends AdminController
21 { 19 {
20 + protected $title = '制作历史记录';
21 +
22 /** 22 /**
23 * Make a grid builder. 23 * Make a grid builder.
24 * 24 *
...@@ -26,33 +26,36 @@ class AdminMakeVideoController extends AdminController ...@@ -26,33 +26,36 @@ class AdminMakeVideoController extends AdminController
26 */ 26 */
27 protected function grid() 27 protected function grid()
28 { 28 {
29 - return Grid::make(new Immerse(), function (Grid $grid) { 29 + return Grid::make(new AdminMakeVideo(), function (Grid $grid) {
30 - $grid->model()->where('user_id','=',1); 30 + // 设置自定义视图
31 - 31 + $grid->setActionClass(Grid\Displayers\Actions::class);
32 $grid->column('id')->sortable(); 32 $grid->column('id')->sortable();
33 - $grid->column('title','标题'); 33 + $grid->column('type','类型')->using([1 => '视频', 2 => '音频']);
34 - $grid->column('content','有感'); 34 +
35 - $grid->column('url')->display(function ($url){ 35 + $grid->column('video_url')->display(function ($url){
36 - return "<a target='_blank' href='". $url ."'>查看</a>"; 36 + if ($url == '' || $url == null) return '';
37 + $path = Storage::disk('public')->url($url);
38 + return "<a target='_blank' href='". $path ."'>查看</a>";
39 + });
40 +
41 + $grid->column('images_url')->display(function ($url){
42 + if ($url == '' || $url == null) return '';
43 + $path = Storage::disk('public')->url($url);
44 + return "<a target='_blank' href='". $path ."'>查看</a>";
37 }); 45 });
38 - $grid->column('type','类型')->using([1 => '音频', 2 => '视频']); 46 +
39 - $grid->column('duration'); 47 + $grid->column('feel','有感');
40 - $grid->column('size'); 48 + $grid->column('weather','天气');
49 + $grid->column('huangli','黄历');
41 $grid->column('poem_id'); 50 $grid->column('poem_id');
42 $grid->column('temp_id'); 51 $grid->column('temp_id');
43 - $grid->column('thumbnail')->image(); 52 + $grid->column('thumbnail');
44 - $grid->column('bgm')->display(function ($url){ 53 + $grid->column('thumbnail_url')->image();
45 - if (Str::of($url)->contains('.mp3')) 54 +
46 - return "<a target='_blank' href='". $url ."'>查看</a>";
47 - else
48 - return "<a target='_blank' href='". $url ."'>下载</a>";
49 - });
50 $grid->column('created_at'); 55 $grid->column('created_at');
51 $grid->column('updated_at')->sortable(); 56 $grid->column('updated_at')->sortable();
52 -
53 $grid->filter(function (Grid\Filter $filter) { 57 $grid->filter(function (Grid\Filter $filter) {
54 $filter->equal('id'); 58 $filter->equal('id');
55 -
56 }); 59 });
57 }); 60 });
58 } 61 }
...@@ -93,9 +96,10 @@ class AdminMakeVideoController extends AdminController ...@@ -93,9 +96,10 @@ class AdminMakeVideoController extends AdminController
93 return Form::make(new AdminMakeVideo(), function (Form $form) { 96 return Form::make(new AdminMakeVideo(), function (Form $form) {
94 $form->display('id'); 97 $form->display('id');
95 98
96 - $form->selectTable('poem_id','选择一言') 99 + $form->selectTable('temp_id','选择模板')
97 - ->title('一言诗词库') 100 + ->title('模板选择')
98 - ->from(PoemTable::make()); 101 + ->from(TemplateTable::make())
102 + ->model(VideoTemp::class,'id','title');
99 103
100 $form->radio('type') 104 $form->radio('type')
101 ->options([1=>'视频', 2=>'图文音频']) 105 ->options([1=>'视频', 2=>'图文音频'])
...@@ -109,36 +113,25 @@ class AdminMakeVideoController extends AdminController ...@@ -109,36 +113,25 @@ class AdminMakeVideoController extends AdminController
109 ->addElementClass('video_url'); 113 ->addElementClass('video_url');
110 }) 114 })
111 ->when(2,function (Form $form){ 115 ->when(2,function (Form $form){
112 - $form->multipleImage('images_url','上传图片') 116 + $form->image('images_url','上传图片')
113 - ->limit(5)
114 ->uniqueName() 117 ->uniqueName()
115 ->addElementClass('images_url'); 118 ->addElementClass('images_url');
116 }) 119 })
117 ->default(1); 120 ->default(1);
118 - $form->radio('bg_music','背景音')
119 - ->options(['无', '有'])
120 - ->when(1,function (Form $form){
121 - $form->file('bgm_url','上传背景音')
122 - ->accept('mp3,aac,wav')
123 - ->autoUpload()
124 - ->uniqueName()
125 - ->addElementClass('bg_music');
126 - })
127 - ->default(0);
128 121
122 + $form->selectTable('poem_id','选择一言')
123 + ->title('一言诗词库')
124 + ->from(PoemTable::make())
125 + ->model(OnePoem::class,'id','title');
129 $form->textarea('feel','有感'); 126 $form->textarea('feel','有感');
130 - 127 + $form->text('weather','天气');
131 - $form->selectTable('temp_id','选择模板') 128 + $form->text('huangli','黄历')->default('宜');
132 - ->title('模板选择')
133 - ->from(TemplateTable::make());
134 129
135 $form->radio('thumbnail','封面') 130 $form->radio('thumbnail','封面')
136 ->options([1=>'手动上传', 2=>'自动截屏']) 131 ->options([1=>'手动上传', 2=>'自动截屏'])
137 ->when(1,function (Form $form){ 132 ->when(1,function (Form $form){
138 - $form->multipleImage('thumbnail_url','上传图片') 133 + $form->image('thumbnail_url','上传图片')
139 - ->limit(5)
140 ->uniqueName(); 134 ->uniqueName();
141 -// ->addElementClass('bg_img_url');
142 }) 135 })
143 ->when(2,function (Form $form){ 136 ->when(2,function (Form $form){
144 $form->html(''); 137 $form->html('');
...@@ -147,31 +140,12 @@ class AdminMakeVideoController extends AdminController ...@@ -147,31 +140,12 @@ class AdminMakeVideoController extends AdminController
147 140
148 $form->display('created_at'); 141 $form->display('created_at');
149 $form->display('updated_at'); 142 $form->display('updated_at');
150 - });
151 - }
152 143
153 - public function store() 144 + $form->saved(function (Form $form, $result) {
154 - { 145 + $model = $form->repository()->model();
155 - $all = request()->all(); 146 + AdminMakeImmerse::dispatch($model);
156 - 147 + });
157 - if (isset($all['upload_column'])) return $this->form()->store(); 148 + });
158 -
159 - try{
160 - $video = AdminMakeVideo::query()->create($all);
161 - if ($all['type'] == 1){
162 - // 添加至队列
163 - MakeVideo::dispatch($video);
164 - }elseif ($all['type'] == 2){ // 新增类型,不止视频,还有静态图。
165 - // MakeImages
166 - MakeImages::dispatch($video);
167 - }else{
168 - return $this->form()->response()->error('类型选择错误');
169 - }
170 - }catch (\Exception $exception){
171 - return $this->form()->response()->error($exception->getMessage());
172 - }
173 -
174 - return $this->form()->response()->refresh()->success(trans('admin.save_succeeded'));
175 } 149 }
176 150
177 public function destroy($id) 151 public function destroy($id)
......
1 +<?php
2 +
3 +namespace App\Admin\Controllers;
4 +
5 +use App\Admin\Renderable\PoemTable;
6 +use App\Admin\Renderable\TemplateTable;
7 +use App\Jobs\AdminMakeImmerse;
8 +use App\Jobs\MakeImages;
9 +use App\Jobs\MakeVideo;
10 +use App\Admin\Repositories\AdminMakeVideo;
11 +use App\Models\Immerse;
12 +use App\Models\OnePoem;
13 +use App\Models\VideoTemp;
14 +use Dcat\Admin\Form;
15 +use Dcat\Admin\Grid;
16 +use Dcat\Admin\Show;
17 +use Dcat\Admin\Http\Controllers\AdminController;
18 +use Illuminate\Support\Facades\Storage;
19 +use Illuminate\Support\Str;
20 +
21 +class ImmerseController extends AdminController
22 +{
23 + /**
24 + * Make a grid builder.
25 + *
26 + * @return Grid
27 + */
28 + protected function grid()
29 + {
30 + return Grid::make(new Immerse(), function (Grid $grid) {
31 + $grid->model()->where('user_id','=',1);
32 +
33 + // 设置自定义视图
34 + $grid->setActionClass(Grid\Displayers\Actions::class);
35 +
36 + $grid->column('id')->sortable();
37 + $grid->column('content','有感');
38 + $grid->column('url')->display(function ($url){
39 + return "<a target='_blank' href='". $url ."'>查看</a>";
40 + });
41 + $grid->column('type','类型')->using([1 => '音频', 2 => '视频']);
42 + $grid->column('duration','时长');
43 + $grid->column('size','大小');
44 + $grid->column('poem_id');
45 + $grid->column('temp_id');
46 + $grid->column('thumbnail')->image();
47 + $grid->column('bgm')->display(function ($url){
48 + if (Str::of($url)->contains('.mp3'))
49 + return "<a target='_blank' href='". $url ."'>查看</a>";
50 + else
51 + return "<a target='_blank' href='". $url ."'>下载</a>";
52 + });
53 + $grid->column('created_at');
54 + $grid->column('updated_at')->sortable();
55 +
56 + $grid->filter(function (Grid\Filter $filter) {
57 + $filter->equal('id');
58 +
59 + });
60 + });
61 + }
62 +
63 + public function destroy($id)
64 + {
65 + $immerse = Immerse::query()->find($id);
66 +
67 + Storage::disk('public')->delete($immerse->url);
68 + Storage::disk('public')->delete($immerse->thumbnail);
69 + Storage::disk('public')->delete($immerse->bgm);
70 + $immerse->delete();
71 +
72 + return $this->form()->response()->refresh()->success('删除成功');
73 + }
74 +}
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
2 2
3 namespace App\Admin\Controllers; 3 namespace App\Admin\Controllers;
4 4
5 +use App\Admin\Renderable\FontTable;
6 +use App\Models\Font;
5 use App\Models\VideoTemp; 7 use App\Models\VideoTemp;
6 use App\Models\Component; 8 use App\Models\Component;
7 use Dcat\Admin\Form; 9 use Dcat\Admin\Form;
...@@ -47,22 +49,9 @@ class VideoTempController extends AdminController ...@@ -47,22 +49,9 @@ class VideoTempController extends AdminController
47 $th = ['id','模板id','名称','位置','字号','字体颜色','背景色','背景厚度','透明度','避免剪切','创建时间','修改时间']; 49 $th = ['id','模板id','名称','位置','字号','字体颜色','背景色','背景厚度','透明度','避免剪切','创建时间','修改时间'];
48 return Table::make($th, $this->components->toArray())->withBorder(); 50 return Table::make($th, $this->components->toArray())->withBorder();
49 }); 51 });
50 -// $grid->column('type');
51 -// $grid->column('bg_type');
52 -// $grid->column('bg_url')->image('/storage/');
53 -// $grid->column('bg_music');
54 $grid->column('state')->switch(); 52 $grid->column('state')->switch();
55 -// $grid->column('sn');
56 -// $grid->column('top');
57 -// $grid->column('left');
58 -// $grid->column('font_size');
59 $grid->column('created_at'); 53 $grid->column('created_at');
60 $grid->column('updated_at')->sortable(); 54 $grid->column('updated_at')->sortable();
61 -//
62 -// $grid->filter(function (Grid\Filter $filter) {
63 -// $filter->equal('id');
64 -//
65 -// });
66 }); 55 });
67 } 56 }
68 57
...@@ -99,7 +88,7 @@ class VideoTempController extends AdminController ...@@ -99,7 +88,7 @@ class VideoTempController extends AdminController
99 */ 88 */
100 protected function form() 89 protected function form()
101 { 90 {
102 - return Form::make(new VideoTemp(), function (Form $form) { 91 + return Form::make(VideoTemp::with('components'), function (Form $form) {
103 $form->display('id'); 92 $form->display('id');
104 $form->block(7, function (Form\BlockForm $form) { 93 $form->block(7, function (Form\BlockForm $form) {
105 // 设置标题 94 // 设置标题
...@@ -122,97 +111,33 @@ class VideoTempController extends AdminController ...@@ -122,97 +111,33 @@ class VideoTempController extends AdminController
122 }) 111 })
123 ->default(0); 112 ->default(0);
124 113
125 - $form->checkbox('components','组件') 114 +
126 - ->when('every_poem', $this->buildCheckBoxOption('every_poem',$form)) 115 + $form->hasMany('components','组件', function (Form\NestedForm $form) {
127 - ->when('one_poem', $this->buildCheckBoxOption('one_poem',$form)) 116 + $form->select('name','组件名称')->options([
128 - ->when('weather', $this->buildCheckBoxOption('weather',$form))
129 - ->when('date', $this->buildCheckBoxOption('date',$form))
130 - ->when('feel', $this->buildCheckBoxOption('feel',$form))
131 - ->default(['one_poem','weather','date'])
132 - ->options([
133 'every_poem' => '每日一言组件', 117 'every_poem' => '每日一言组件',
134 'one_poem' => '一言组件', 118 'one_poem' => '一言组件',
135 'weather' => '天气组件', 119 'weather' => '天气组件',
136 'date' => '日期组件', 120 'date' => '日期组件',
137 'feel' => '临境有感组件', 121 'feel' => '临境有感组件',
138 ]); 122 ]);
123 + $form->select('position','组件位置')->options(VideoTemp::POSITION_OPTIONS);
124 + $form->number('text_bg_box', '背景厚度')->default(0)
125 + ->addElementClass('text_bg_box')->help('设置背景块边缘厚度(用于在背景块边缘用背景色填充一圈),默认为0');
126 + $form->color('text_bg_color', '背景色')->default('#5c6bc6')->addElementClass('text_bg_color');
127 + $form->selectTable('font_file','字体')
128 + ->title('字体选择')
129 + ->from(FontTable::make())
130 + ->model(Font::class,'file','name');
131 + $form->number('font_size', '字号')->default(12)->min(12);
132 + $form->color('text_color', '字体颜色')->default('#f5f5f5')->addElementClass('text_color');
133 + $form->number('opacity', '透明度')->min(0)->max(100)
134 + ->addElementClass('opacity')->default(100)
135 + ->help('范围为0-100,100表示不透明,0表示完全透明');
136 + $form->switch('fix_bounds', '避免剪切');
139 137
140 - $form->hidden('state')
141 - ->saving(function ($v) {
142 - return $v;
143 - });
144 - });
145 -
146 - $form->block(5, function (Form\BlockForm $form) {
147 - $form->html(view('admin.form.phone'));
148 - });
149 -
150 - $form->display('created_at');
151 - $form->display('updated_at');
152 }); 138 });
153 - }
154 -
155 -
156 - public function edit($id, Content $content)
157 - {
158 - return $content
159 - ->translation($this->translation())
160 - ->title($this->title())
161 - ->description($this->description()['edit'] ?? trans('admin.edit'))
162 - ->body($this->form2()->edit($id));
163 - }
164 -
165 - public function form2()
166 - {
167 - return Form::make(VideoTemp::with('components'), function (Form $form) {
168 -
169 -// dd($form->model()->toArray());
170 -
171 - $form->display('id');
172 - $form->block(7, function (Form\BlockForm $form) {
173 - // 设置标题
174 - $form->title('基本设置');
175 - // 显示底部提交按钮
176 - $form->showFooter();
177 - // 设置字段宽度
178 - $form->width(8, 3);
179 -
180 - $form->text('title');
181 -
182 - $form->radio('bg_music')
183 - ->options(['无', '有'])
184 - ->when(1,function (Form\BlockForm $form){
185 - $form->file('bgm_url')
186 - ->accept('mp3,aac,wav')
187 - ->autoUpload()
188 - ->uniqueName()
189 - ->addElementClass('bgm_url');
190 - })
191 - ->default(0);
192 139
193 - $form->checkbox('components','组件') 140 + $form->hidden('state')->default(1)
194 -// ->when('every_poem', $this->buildCheckBoxOption('every_poem',$form))
195 -// ->when('one_poem', $this->buildCheckBoxOption('one_poem',$form))
196 -// ->when('weather', $this->buildCheckBoxOption('weather',$form))
197 -// ->when('date', $this->buildCheckBoxOption('date',$form))
198 -// ->when('feel', $this->buildCheckBoxOption('feel',$form))
199 - ->default(['one_poem','weather','date'])
200 - ->options([
201 - 'every_poem' => '每日一言组件',
202 - 'one_poem' => '一言组件',
203 - 'weather' => '天气组件',
204 - 'date' => '日期组件',
205 - 'feel' => '临境有感组件',
206 - ])
207 - ->customFormat(function ($v) {
208 - if (! $v) {
209 - return [];
210 - }
211 -
212 - return array_column($v, 'id');
213 - });;
214 -
215 - $form->hidden('state')
216 ->saving(function ($v) { 141 ->saving(function ($v) {
217 return $v; 142 return $v;
218 }); 143 });
...@@ -226,76 +151,4 @@ class VideoTempController extends AdminController ...@@ -226,76 +151,4 @@ class VideoTempController extends AdminController
226 $form->display('updated_at'); 151 $form->display('updated_at');
227 }); 152 });
228 } 153 }
229 -
230 -
231 - public function store()
232 - {
233 - $all = \request()->all();
234 -
235 - try{
236 - DB::transaction(function ()use ($all){
237 - $vide_temp = VideoTemp::query()->create([
238 - 'title' => $all['title'],
239 - 'state' => 1,
240 - ]);
241 - foreach ($all['components'] as $component) {
242 -
243 - if ($component !== null){
244 - Component::query()->create([
245 - 'temp_id' => $vide_temp->id,
246 - 'name' => $component,
247 - 'position' => $all['pos_' . $component],
248 - 'font_size' => $all['font_size_' . $component],
249 - 'text_color' => $all['text_color_' . $component],
250 - 'text_bg_color' => $all['text_bg_color_' . $component],
251 - 'text_bg_box' => $all['text_bg_box_' . $component],
252 - 'opacity' => $all['opacity_' . $component],
253 - 'fix_bounds' => $all['fix_bounds_' . $component],
254 - ]);
255 - }
256 - }
257 - });
258 -
259 - }catch (\Exception $exception){
260 - return $this->form()->response()->error($exception->getMessage());
261 - }
262 -
263 - return $this->form()->response()->refresh()->success(trans('admin.save_succeeded'));
264 - }
265 -
266 - public function buildCheckBoxOption($prefix, Form\BlockForm $form)
267 - {
268 - return function ()use ($prefix, $form) {
269 - switch ($prefix) {
270 - case 'every_poem':
271 - $label = '每日一言位置';
272 - break;
273 - case 'one_poem':
274 - $label = '一言位置';
275 - break;
276 - case 'weather':
277 - $label = '天气位置';
278 - break;
279 - case 'date':
280 - $label = '日期位置';
281 - break;
282 - case 'feel':
283 - $label = '有感位置';
284 - break;
285 - default:
286 - $label = '组件位置';
287 - }
288 - $form->divider();
289 - $form->select('pos_' . $prefix, $label)->options(VideoTemp::POSITION_OPTIONS);
290 - $form->number('text_bg_box_' . $prefix, '背景厚度')->default(0)
291 - ->addElementClass('text_bg_box_' . $prefix)->help('设置背景块边缘厚度(用于在背景块边缘用背景色填充一圈),默认为0');
292 - $form->color('text_bg_color_' . $prefix, '背景色')->default('#5c6bc6')->addElementClass('text_bg_color_' . $prefix);
293 - $form->number('font_size_' . $prefix, '字号')->min(12);
294 - $form->color('text_color_' . $prefix, '字体颜色')->default('#f5f5f5')->addElementClass('text_color_' . $prefix);
295 - $form->number('opacity_' . $prefix, '透明度')->min(0)->max(100)
296 - ->addElementClass('opacity_' . $prefix)->default(100)
297 - ->help('范围为0-100,100表示不透明,0表示完全透明');
298 - $form->switch('fix_bounds_' . $prefix, '避免剪切');
299 - };
300 - }
301 } 154 }
......
1 +<?php
2 +/**
3 + * Created by PhpStorm.
4 + * User: lishuai
5 + * Date: 2022/1/10
6 + * Time: 5:57 PM
7 + */
8 +
9 +namespace App\Admin\Renderable;
10 +
11 +use App\Admin\Repositories\OnePoem;
12 +use App\Models\Font;
13 +use Dcat\Admin\Grid;
14 +use Dcat\Admin\Grid\LazyRenderable;
15 +use Illuminate\Support\Facades\Storage;
16 +
17 +class FontTable extends LazyRenderable
18 +{
19 +
20 + public function grid(): Grid
21 + {
22 + return Grid::make(new Font(), function (Grid $grid) {
23 + $grid->paginate(10);
24 + $grid->disableActions();
25 + $grid->quickSearch(['name']);
26 + $grid->column('id')->sortable();
27 + $grid->column('name');
28 + $grid->column('file','字体')->display(function ($item){
29 + $url = Storage::disk('public')->url($item);
30 + return "<style>
31 +@font-face {
32 + font-family: 'ParlandoFont{$this->id}';
33 + src: url('{$url}') format('truetype');
34 + font-weight: normal;
35 + font-style: normal;
36 +}
37 +.mfont-{$this->id}{
38 + font-family: 'ParlandoFont{$this->id}';
39 + color: red;
40 +
41 +}
42 +</style><span class='mfont-{$this->id} fa-2x'>字体示例:这里是临境有感</span>";
43 +
44 + });
45 + $grid->column('created_at');
46 + $grid->column('updated_at')->sortable();
47 +
48 + $grid->filter(function (Grid\Filter $filter) {
49 + $filter->like('name','名称');
50 + });
51 + });
52 + }
53 +
54 +// public function grid(): Grid
55 +// {
56 +// return Grid::make(new OnePoem(), function (Grid $grid) {
57 +// $grid->column('id', 'ID')->sortable();
58 +// $grid->column('title');
59 +// $grid->column('author');
60 +// $grid->column('content');
61 +// $grid->column('annotate');
62 +//// $grid->column('spelling');
63 +//// $grid->column('en');
64 +//
65 +// $grid->quickSearch(['title', 'author', 'content', 'annotate']);
66 +//
67 +// $grid->paginate(10);
68 +// $grid->disableActions();
69 +//
70 +// $grid->filter(function (Grid\Filter $filter) {
71 +// $filter->like('title')->width(3);
72 +// $filter->like('author')->width(3);
73 +// $filter->like('content')->width(3);
74 +// });
75 +// });
76 +
77 +
78 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -28,7 +28,8 @@ Route::group([ ...@@ -28,7 +28,8 @@ Route::group([
28 $router->group(['prefix'=>'/linjing'],function (Router $router){ 28 $router->group(['prefix'=>'/linjing'],function (Router $router){
29 $router->resource('/font', 'FontController'); 29 $router->resource('/font', 'FontController');
30 $router->resource('/template', 'VideoTempController'); 30 $router->resource('/template', 'VideoTempController');
31 - $router->resource('/official', 'AdminMakeVideoController'); 31 + $router->resource('/make', 'AdminMakeVideoController');
32 + $router->resource('/official', 'ImmerseController');
32 }); 33 });
33 34
34 /** 订单*/ 35 /** 订单*/
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
2 2
3 namespace App\Console\Commands; 3 namespace App\Console\Commands;
4 4
5 +use App\Jobs\AdminMakeImmerse;
5 use App\Jobs\MakeImages; 6 use App\Jobs\MakeImages;
6 use App\Jobs\UserMakeImmerse; 7 use App\Jobs\UserMakeImmerse;
7 use App\Models\AdminMakeVideo; 8 use App\Models\AdminMakeVideo;
...@@ -65,6 +66,10 @@ class DevFFmpeg extends Command ...@@ -65,6 +66,10 @@ class DevFFmpeg extends Command
65 */ 66 */
66 public function handle() 67 public function handle()
67 { 68 {
69 + dd(AdminMakeVideo::query()->find(33)->temp->components->toArray());
70 + AdminMakeImmerse::dispatch(AdminMakeVideo::query()->find(33)->temp->components);
71 +
72 +
68 dd(1); 73 dd(1);
69 // 分情况 1.用户视频,2.用户录音 74 // 分情况 1.用户视频,2.用户录音
70 // 注意事项:1.考虑用户是否会员;非会员分辨率 720x1280,会员分辨率1440x2560 75 // 注意事项:1.考虑用户是否会员;非会员分辨率 720x1280,会员分辨率1440x2560
......
1 +<?php
2 +
3 +namespace App\Jobs;
4 +
5 +use App\Models\Immerse;
6 +use App\Models\VideoTemp;
7 +use Illuminate\Bus\Queueable;
8 +use Illuminate\Contracts\Queue\ShouldBeUnique;
9 +use Illuminate\Contracts\Queue\ShouldQueue;
10 +use Illuminate\Foundation\Bus\Dispatchable;
11 +use Illuminate\Queue\InteractsWithQueue;
12 +use Illuminate\Queue\SerializesModels;
13 +use App\Models\AdminMakeVideo;
14 +use Illuminate\Support\Carbon;
15 +use Illuminate\Support\Facades\Log;
16 +use Illuminate\Support\Facades\Storage;
17 +
18 +class AdminMakeImmerse implements ShouldQueue
19 +{
20 + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
21 +
22 + public $adminMakeVideo;
23 +
24 + protected $ffmpeg;
25 +
26 + protected $ffprobe;
27 +
28 + protected $output_width;
29 +
30 + protected $output_height;
31 +
32 + /**
33 + * Create a new job instance.
34 + * @param AdminMakeVideo $adminMakeVideo
35 + * @return void
36 + */
37 + public function __construct(AdminMakeVideo $adminMakeVideo)
38 + {
39 + $this->adminMakeVideo = $adminMakeVideo;
40 + $this->ffmpeg = env('FFMPEG_CMD');
41 + $this->ffprobe = env('FFPROBE_CMD');
42 + $this->output_width = 720;
43 + $this->output_height = 1280;
44 + }
45 +
46 + /**
47 + * Execute the job.
48 + *
49 + * @return void
50 + */
51 + public function handle()
52 + {
53 + $adminMakeVideo = $this->adminMakeVideo;
54 +
55 + // 模板
56 + $template = $adminMakeVideo->temp->first();
57 +
58 + // 素材准备
59 + $drawtext = $this->getTextContentString();
60 + $watermark = $this->getAbsolutePath('ffmpeg/LOGO_eng.png');
61 + $is_bgm = $template->bg_music;
62 + $bgm = $this->getAbsolutePath($template->bgm_url);
63 +
64 + // 区分类型
65 + if ($adminMakeVideo->type == 1) { // 视频
66 + $file = $this->getAbsolutePath($adminMakeVideo->video_url);
67 + // 分析视频
68 + $media_info = $this->mediainfo($file);
69 +
70 + if ($media_info['format']['nb_streams'] >= 2) {
71 + /** 音频视频轨都有 */
72 + if ($is_bgm) {
73 + // 有背景音 融合
74 + $audio = $this->getAbsolutePath($this->getTempPath('.mp3'));
75 + $cmd = $this->ffmpeg .
76 + ' -y -i ' . escapeshellarg($file) .
77 + ' -y -i ' . escapeshellarg($bgm) .
78 + ' -filter_complex amix=inputs=2:duration=first:dropout_transition=2 ' .
79 + '-ar 48000 -ab 64k ' . escapeshellarg($audio);
80 + if (!$this->execmd($cmd)) return;
81 +
82 + $audio_input = ' -i ' . escapeshellarg($audio);
83 + $audio_filter = '2:a';
84 + } else {
85 + // 没有背景音
86 + $audio_input = '';
87 + $audio_filter = '0:a';
88 + }
89 + } elseif ($media_info['format']['nb_streams'] == 1) {
90 + /** 只有视频轨 */
91 + // 生成一段无声音频
92 + $audio = $this->getAbsolutePath($this->getTempPath('.mp3'));
93 + $cmd = $this->ffmpeg .
94 + ' -y -f lavfi -i aevalsrc=0:duration=' . escapeshellarg($media_info['format']['duration']) .
95 + ' -ar 48000 -ab 64k ' . escapeshellarg($audio);
96 + if (!$this->execmd($cmd)) return;
97 +
98 + if ($is_bgm) {
99 + // 有背景音 融合
100 + $audio_empty = $audio;
101 + $audio = $this->getAbsolutePath($this->getTempPath('.mp3'));
102 + $cmd = $this->ffmpeg .
103 + ' -y -i ' . escapeshellarg($audio_empty) .
104 + ' -y -i ' . escapeshellarg($bgm) .
105 + ' -filter_complex amix=inputs=2:duration=first:dropout_transition=2 ' .
106 + '-ar 48000 -ab 64k ' . escapeshellarg($audio);
107 + if (!$this->execmd($cmd)) return;
108 + }
109 + $audio_input = ' -i ' . escapeshellarg($audio);
110 + $audio_filter = '2:a';
111 + } else {
112 + /** 音频视频轨都没有 */
113 + Log::channel('daily')->error('视频没有video track');
114 + return;
115 + }
116 +
117 + $thumbnail = $this->getTempPath('.jpg',false);
118 + if ($adminMakeVideo->thumbnail == 2){
119 + // 截取中间帧作为视频封面
120 + $frame = ceil($media_info['streams'][0]['nb_frames'] / 2);
121 + $cmd = $this->ffmpeg . ' -y ' .
122 + ' -i ' . escapeshellarg($file) .
123 + ' -filter_complex "[0:v]select=\'eq(n,' . $frame . ')\'[img]" ' .
124 + ' -map [img]'.
125 + ' -frames:v 1 -s ' . $this->output_width . 'x' . $this->output_height . ' -preset superfast ' .
126 + escapeshellarg($this->getAbsolutePath($thumbnail));
127 + if (!$this->execmd($cmd)) return ;
128 + }else{
129 + // 手动上传封面
130 + $origin_thumbnail = Storage::disk('public')->path($adminMakeVideo->thumbnail_url);
131 + // 将封面分辨率改为指定分辨率
132 + $cmd = $this->ffmpeg . ' -y ' .
133 + ' -i ' . escapeshellarg($origin_thumbnail) .
134 + '-s ' . $this->output_width . 'x' . $this->output_height . ' -preset superfast ' .
135 + escapeshellarg($this->getAbsolutePath($thumbnail));
136 + if (!$this->execmd($cmd)) return ;
137 + }
138 +
139 + $output = $this->getTempPath('.mp4',false);
140 + $cmd = $this->ffmpeg . ' -y '.
141 + ' -i ' . escapeshellarg($file).
142 + ' -i ' . escapeshellarg($watermark).
143 + $audio_input .
144 + ' -filter_complex "[0:0]scale=' . $this->output_width . ':' . $this->output_height . ',' . $drawtext .
145 + ' [text];[text]'.
146 + ' [1:v]overlay=20:20[v]" ' .
147 + ' -map [v] -map '. $audio_filter .
148 + ' -c:v libx264 -bt 256k -r 25' .
149 + ' -ar 44100 -ac 2 -qmin 30 -qmax 60 -profile:v baseline -preset fast ' .
150 + escapeshellarg($this->getAbsolutePath($output));
151 +
152 + if (!$this->execmd($cmd)) return ;
153 +
154 + $video_info = $this->mediainfo($this->getAbsolutePath($output));
155 + Immerse::query()->create([
156 + 'user_id' => 1,
157 + 'title' => '',
158 + 'content' => $adminMakeVideo->feel,
159 + 'url' => $output,
160 + 'type' => $adminMakeVideo->type == 1 ? 2 : 1,
161 + 'upload_file' => '',
162 + 'duration' => $video_info['format']['duration'],
163 + 'size' => $video_info['format']['size'],
164 + 'origin_video_url' => $this->adminMakeVideo->video_url,
165 + 'origin_image_url' => '',
166 + 'poem_id' => $this->adminMakeVideo->poem_id,
167 + 'temp_id' => $this->adminMakeVideo->temp_id,
168 + 'thumbnail' => $thumbnail,
169 + 'state' => 1,
170 + 'bgm' => $bgm ?? '',
171 + ]);
172 +
173 + }else{ // 图文
174 + $image = $this->getAbsolutePath($adminMakeVideo->images_url);
175 +
176 + if ($this->adminMakeVideo->type == 2 && $is_bgm == 0){
177 + // 没有背景音,单图一张,输出为单图。
178 + $output = $this->getTempPath('.png',false);
179 + $cmd = $this->ffmpeg . ' -y '.
180 + ' -i ' . escapeshellarg($image).
181 + ' -i ' . escapeshellarg($watermark).
182 + ' -filter_complex "[0:0]scale=' . $this->output_width . ':' . $this->output_height . ',' .
183 + $drawtext . ' [text];[text][1:0]overlay=20:20" ' .
184 + escapeshellarg($this->getAbsolutePath($output));
185 + if (!$this->execmd($cmd)) return ;
186 + $thumbnail = $output;
187 + }else{
188 + // 有背景音 单图合成视频,时长为音频时长,音频加入背景音
189 + $output = $this->getTempPath('.mp4',false);
190 +
191 + // 分析背景音
192 + $mediainfo = $this->mediainfo($bgm);
193 + // 记录媒体信息时长
194 + $duration = $mediainfo['format']['duration'] ?: 0;
195 +
196 + // 单图、水印、bgm 合成视频
197 + $cmd = $this->ffmpeg . ' -y ' .
198 + ' -loop 1 -i ' . escapeshellarg($image) .
199 + ' -i ' . escapeshellarg($watermark) .
200 + ' -i ' . escapeshellarg($bgm) .
201 + ' -filter_complex "[0:v]scale=' . $this->output_width . ':' . $this->output_height . ',setdar=dar=9/16,' . $drawtext .
202 + ' [text];[text][2:v]overlay=20:20[v]"' .
203 + ' -map [v] -map 1:0 ' .
204 + ' -c:v libx264 -bt 256k -r 25 -t ' . $duration .
205 + ' -ar 48000 -ac 2 -qmin 30 -qmax 60 -profile:v high -pix_fmt yuv420p -preset fast '.
206 + escapeshellarg($this->getAbsolutePath($output));
207 + if (!$this->execmd($cmd)) return ;
208 +
209 + $thumbnail = $this->getTempPath('.jpg',false);
210 + if ($adminMakeVideo->thumbnail == 2){
211 + // 将封面分辨率改为指定分辨率
212 + $cmd = $this->ffmpeg . ' -y ' .
213 + ' -i ' . escapeshellarg($image) .
214 + '-s ' . $this->output_width . 'x' . $this->output_height . ' -preset superfast ' .
215 + escapeshellarg($this->getAbsolutePath($thumbnail));
216 + if (!$this->execmd($cmd)) return ;
217 + }else{
218 + // 手动上传封面
219 + $origin_thumbnail = $this->getAbsolutePath($adminMakeVideo->thumbnail_url);
220 + // 将封面分辨率改为指定分辨率
221 + $cmd = $this->ffmpeg . ' -y ' .
222 + ' -i ' . escapeshellarg($origin_thumbnail) .
223 + '-s ' . $this->output_width . 'x' . $this->output_height . ' -preset superfast ' .
224 + escapeshellarg($this->getAbsolutePath($thumbnail));
225 + if (!$this->execmd($cmd)) return ;
226 + }
227 + }
228 +
229 + // 全部合成以后创建 临境
230 + $video_info = $this->mediainfo($output);
231 +
232 + Immerse::query()->create([
233 + 'user_id' => 1,
234 + 'title' => '',
235 + 'content' => $this->adminMakeVideo->feel,
236 + 'url' => $output,
237 + 'type' => $this->adminMakeVideo->type == 1 ? 2 : 1,
238 + 'upload_file' => '',
239 + 'duration' => $video_info['format']['duration'] ?? 0,
240 + 'size' => $video_info['format']['size'] ?? 0,
241 + 'origin_video_url' => '',
242 + 'origin_image_url' => $this->adminMakeVideo->images_url,
243 + 'poem_id' => $this->adminMakeVideo->poem_id,
244 + 'temp_id' => $this->adminMakeVideo->temp_id,
245 + 'thumbnail' => $thumbnail,
246 + 'state' => 1,
247 + 'bgm' => $bgm ?? '',
248 + ]);
249 + }
250 +
251 + }
252 +
253 + public function mediainfo($file)
254 + {
255 + $cmd = $this->ffprobe . ' -v quiet -print_format json -show_format -show_streams ' . escapeshellarg($file);
256 + $output = $this->execmd($cmd);
257 + $data = json_decode($output, true);
258 + if (json_last_error() === JSON_ERROR_UTF8) {
259 + $output = mb_convert_encoding($output, "UTF-8");
260 + $data = json_decode($output, true);
261 + }
262 + return $data;
263 + }
264 +
265 + public function execmd($cmd, $update_progress = false) {
266 + echo $cmd . "\n". "\n". "\n";
267 + $descriptorspec = array(
268 + 1 => array("pipe", "w"), // 标准输出,子进程向此管道中写入数据
269 + );
270 + $process = proc_open("{$cmd} 2>&1", $descriptorspec, $pipes);
271 + if (is_resource($process)) {
272 + $error0 = '';
273 + $error1 = '';
274 + $stdout = '';
275 + while (!feof($pipes[1])) {
276 + $line = fgets($pipes[1], 150);
277 + $stdout .= $line;
278 + if ($line) {
279 + //记录错误
280 + $error0 = $error1;
281 + $error1 = $line;
282 + if ($update_progress &&
283 + false !== strpos($line, 'size=') &&
284 + false !== strpos($line, 'time=') &&
285 + false !== strpos($line, 'bitrate='))
286 + {
287 + //记录进度 size= 3142kB time=00:00:47.22 bitrate= 545.1kbits/s
288 + $line = explode(' ', $line);
289 + $time = null;
290 + foreach ($line as $item) {
291 + $item = explode('=', $item);
292 + if (isset($item[0]) && isset($item[1]) && $item[0] == 'time') {
293 + $time = $item[1];
294 + break;
295 + }
296 + }
297 + }
298 + }
299 + }
300 + // 切记:在调用 proc_close 之前关闭所有的管道以避免死锁。
301 + fclose($pipes[1]);
302 + $exitedcode = proc_close($process);
303 + if ($exitedcode === 0) {
304 + return $stdout;
305 + } else {
306 + $error = trim($error0,"\n") . ' '. trim($error1,"\n");
307 + // LogUtil::write(array("cmd:{$cmd}", "errno:{$exitedcode}", "stdout:{$stdout}"), __CLASS__);
308 + // ErrorUtil::triggerErrorMsg($error, $exitedcode);
309 + }
310 + } else {
311 + // return ErrorUtil::triggerErrorMsg('proc_open error');
312 + }
313 + }
314 +
315 + /**
316 + * 获取输出临时文件名
317 + * @param string $ext
318 + * @param bool $is_temp
319 + * @return string
320 + */
321 + public function getTempPath($ext = '.mp4',$is_temp = true)
322 + {
323 + $filename = "/output_" . time() . rand(0, 10000);
324 +
325 + $prefix = $is_temp ? 'temp/' : 'video/';
326 + $hash_hex = md5($filename);
327 + // 16进制表示的字符串一共32字节,表示16个二进制字节。
328 + // 前16个字符用来第一级求摸,后16个用做第二级
329 + $hash_hex_l1 = substr($hash_hex, 0, 8);
330 + $hash_hex_l2 = substr($hash_hex, 8, 8);
331 + $dir_l1 = hexdec($hash_hex_l1) % 256;
332 + $dir_l2 = hexdec($hash_hex_l2) % 512;
333 + $dir = $prefix . $dir_l1 . '/' . $dir_l2;
334 +
335 + if( !Storage::disk('public')->exists($dir)) Storage::disk('public')->makeDirectory($dir);
336 +
337 + return $dir . $filename . $ext;
338 + }
339 +
340 +
341 + public function getAbsolutePath($path)
342 + {
343 + if ($path == '') return '';
344 +
345 + return Storage::disk('public')->path($path);
346 + }
347 +
348 + public function getTextContentString()
349 + {
350 + $components = $this->adminMakeVideo->temp->components;
351 +
352 + $drawtext = '';
353 + foreach ($components as $component) {
354 +
355 + $text_color = $component->text_color ?? 'white';
356 + $text_bg_color = $component->text_bg_color ?? '0xd0cdcc';
357 + $opacity = $component->opacity ? $component->opacity / 100 : 0.5;
358 + $font_file = $this->getAbsolutePath($component->font_file ?? 'ffmpeg/arialuni.ttf');
359 + $text_bg_box = $component->text_bg_box ?? 0;
360 + $fix_bounds = $component->fix_bounds == 1;
361 +
362 + switch ($component->name){
363 + case 'every_poem':
364 + case 'one_poem':
365 + $content = $this->adminMakeVideo->poem->content;
366 + $text_file = $this->getAbsolutePath($this->getTempPath('.txt'));
367 + file_put_contents($text_file, $content);
368 + $drawtext .= 'drawtext="'.
369 + 'fontfile=' . escapeshellarg($font_file) . ':' .
370 + 'textfile=' . escapeshellarg($text_file) . ':' .
371 + 'fontsize=' . $this->calcFontSize($component->font_size) . ':' .
372 + 'fontcolor=' . $text_color . '@' . $opacity . ':' .
373 + 'x=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][0]) . ':' .
374 + 'y=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][1]) . ':' .
375 + 'fix_bounds='. $fix_bounds . ':' .
376 + 'box=1:boxborderw='. $text_bg_box . ':' .
377 + 'boxcolor=' . $text_bg_color . '@' . $opacity . '", ';
378 +
379 + break;
380 + case 'weather':
381 + $content = $this->adminMakeVideo->weather;
382 + $drawtext .= 'drawtext="'.
383 + 'fontfile=' . escapeshellarg($font_file) . ':' .
384 + 'text=' . escapeshellarg($content) . ':' .
385 + 'fontsize=' . $this->calcFontSize($component->font_size) . ':' .
386 + 'fontcolor=' . $text_color . '@' . $opacity . ':' .
387 + 'x=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][0]) . ':' .
388 + 'y=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][1]) . ':' .
389 + 'fix_bounds='. $fix_bounds . ':' .
390 + 'box=1:boxborderw='. $text_bg_box . ':' .
391 + 'boxcolor=' . $text_bg_color . '@' . $opacity . '", ';
392 +
393 + break;
394 + case 'date':
395 + $content = Carbon::now()->format('Y年m月d日H时');
396 + $drawtext .= 'drawtext="'.
397 + 'fontfile=' . escapeshellarg($font_file) . ':' .
398 + 'text=' . escapeshellarg($content) . ':' .
399 + 'fontsize=' . $this->calcFontSize($component->font_size) . ':' .
400 + 'fontcolor=' . $text_color . '@' . $opacity . ':' .
401 + 'x=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][0]) . ':' .
402 + 'y=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][1]) . ':' .
403 + 'fix_bounds='. $fix_bounds . ':' .
404 + 'box=1:boxborderw='. $text_bg_box . ':' .
405 + 'boxcolor=' . $text_bg_color . '@' . $opacity . '", ';
406 + break;
407 + case 'feel':
408 + $content = $this->adminMakeVideo->feel ?: '读此一言,仿佛身临其境。';
409 + $drawtext .= 'drawtext="'.
410 + 'fontfile=' . escapeshellarg($font_file) . ':' .
411 + 'text=' . escapeshellarg($content) . ':' .
412 + 'fontsize=' . $this->calcFontSize($component->font_size) . ':' .
413 + 'fontcolor=' . $text_color . '@' . $opacity . ':' .
414 + 'x=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][0]) . ':' .
415 + 'y=' . escapeshellarg(VideoTemp::POSITION_FFMPEG[$component->position][1]) . ':' .
416 + 'fix_bounds='. $fix_bounds . ':' .
417 + 'box=1:boxborderw='. $text_bg_box . ':' .
418 + 'boxcolor=' . $text_bg_color . '@' . $opacity . '", ';
419 + break;
420 + }
421 + }
422 +
423 + return rtrim($drawtext,', ');
424 + }
425 +
426 + public function calcFontSize($width)
427 + {
428 + return ceil($this->output_width / 360 * $width);
429 + }
430 +
431 + public function calcBorderSize($width)
432 + {
433 + return ceil($this->output_width / 360 * $width);
434 + }
435 +
436 + public function getTextHeight()
437 + {
438 + $height = $this->output_height;
439 +
440 + }
441 +}
...@@ -10,4 +10,9 @@ class Component extends Model ...@@ -10,4 +10,9 @@ class Component extends Model
10 use HasFactory; 10 use HasFactory;
11 11
12 protected $guarded = ['']; 12 protected $guarded = [''];
13 +
14 + public function video_temp()
15 + {
16 + return $this->belongsTo('App\Models\VideoTemp','temp_id');
17 + }
13 } 18 }
......
...@@ -11,4 +11,9 @@ class OnePoem extends Model ...@@ -11,4 +11,9 @@ class OnePoem extends Model
11 use HasDateTimeFormatter; 11 use HasDateTimeFormatter;
12 protected $table = 'one_poem'; 12 protected $table = 'one_poem';
13 13
14 +
15 + public function admin_make_video()
16 + {
17 + return $this->belongsTo(AdminMakeVideo::class,'poem_id');
18 + }
14 } 19 }
......
...@@ -11,15 +11,15 @@ class VideoTemp extends Model ...@@ -11,15 +11,15 @@ class VideoTemp extends Model
11 use HasDateTimeFormatter; 11 use HasDateTimeFormatter;
12 12
13 const POSITION_OPTIONS = [ 13 const POSITION_OPTIONS = [
14 - 'topLeft'=>'上左','topMiddle'=>'上中','topRight'=>'上右', 14 + 'topLeft'=>'顶部左对齐','topMiddle'=>'顶部居中','topRight'=>'顶部右对齐',
15 - 'midLeft'=>'中左','midMiddle'=>'中中','midRight'=>'中右', 15 + 'midLeft'=>'居中左对齐','midMiddle'=>'完全居中','midRight'=>'居中右对齐',
16 - 'botLeft'=>'下左','botMiddle'=>'下中','botRight'=>'下右', 16 + 'botLeft'=>'底部左对齐','botMiddle'=>'底部居中','botRight'=>'底部右对齐',
17 ]; 17 ];
18 18
19 const POSITION_FFMPEG = [ 19 const POSITION_FFMPEG = [
20 - 'topLeft' => ['0', 'text_h'], 'topMiddle' => ['(w-text_w)/2', 'text_h'], 'topRight' => ['w-text_w', 'text_h'], 20 + 'topLeft' => ['0', 'text_h+68'], 'topMiddle' => ['(w-text_w)/2', 'text_h+68'], 'topRight' => ['w-text_w', 'text_h+68'],
21 'midLeft' => ['0', '(h-text_h)/2'], 'midMiddle' => ['(w-text_w)/2', '(h-text_h)/2'], 'midRight' => ['w-text_w', '(h-text_h)/2'], 21 'midLeft' => ['0', '(h-text_h)/2'], 'midMiddle' => ['(w-text_w)/2', '(h-text_h)/2'], 'midRight' => ['w-text_w', '(h-text_h)/2'],
22 - 'botLeft' => ['0', 'h-text_h*2'], 'botMiddle' => ['(w-text_w)/2', 'h-text_h*2'], 'botRight' => ['w-text_w', 'h-text_h*2'], 22 + 'botLeft' => ['0', 'h-text_h*2-68'], 'botMiddle' => ['(w-text_w)/2', 'h-text_h*2-68'], 'botRight' => ['w-text_w', 'h-text_h*2-68'],
23 ]; 23 ];
24 24
25 protected $table = 'video_temp'; 25 protected $table = 'video_temp';
...@@ -36,4 +36,9 @@ class VideoTemp extends Model ...@@ -36,4 +36,9 @@ class VideoTemp extends Model
36 return $this->hasMany('App\Models\Component', 'temp_id') 36 return $this->hasMany('App\Models\Component', 'temp_id')
37 ->select(['id', 'temp_id', 'name', 'position', 'font_size', 'text_color', 'text_bg_color', 'text_bg_box','opacity','fix_bounds']); 37 ->select(['id', 'temp_id', 'name', 'position', 'font_size', 'text_color', 'text_bg_color', 'text_bg_box','opacity','fix_bounds']);
38 } 38 }
39 +
40 + public function admin_make_video()
41 + {
42 + return $this->belongsTo(AdminMakeVideo::class,'temp_id');
43 + }
39 } 44 }
......
...@@ -15,6 +15,7 @@ class AlterComponentsTable extends Migration ...@@ -15,6 +15,7 @@ class AlterComponentsTable extends Migration
15 { 15 {
16 16
17 Schema::table('components', function (Blueprint $table) { 17 Schema::table('components', function (Blueprint $table) {
18 + $table->string('font_file')->after('position')->comment('字体文件路径');
18 $table->string('text_bg_box')->after('text_bg_color')->comment('背景厚度'); 19 $table->string('text_bg_box')->after('text_bg_color')->comment('背景厚度');
19 $table->string('fix_bounds')->after('opacity')->comment('超出避免剪切'); 20 $table->string('fix_bounds')->after('opacity')->comment('超出避免剪切');
20 }); 21 });
...@@ -23,6 +24,13 @@ class AlterComponentsTable extends Migration ...@@ -23,6 +24,13 @@ class AlterComponentsTable extends Migration
23 $table->unsignedTinyInteger('bg_music')->after('title')->comment('0=没有,1=有'); 24 $table->unsignedTinyInteger('bg_music')->after('title')->comment('0=没有,1=有');
24 $table->string('bgm_url')->nullable()->after('bg_music')->comment('背景音乐地址'); 25 $table->string('bgm_url')->nullable()->after('bg_music')->comment('背景音乐地址');
25 }); 26 });
27 +
28 + Schema::table('admin_make_video', function (Blueprint $table) {
29 + $table->string('weather')->after('feel')->default('')->comment('天气');
30 + $table->string('huangli')->after('weather')->default('')->comment('黄历');
31 + });
32 +
33 + Schema::dropColumns('admin_make_video', ['bg_music', 'bgm_url']);
26 } 34 }
27 35
28 /** 36 /**
...@@ -33,7 +41,8 @@ class AlterComponentsTable extends Migration ...@@ -33,7 +41,8 @@ class AlterComponentsTable extends Migration
33 public function down() 41 public function down()
34 { 42 {
35 // 43 //
36 - Schema::dropColumns('components', ['text_bg_box', 'fix_bounds']); 44 + Schema::dropColumns('components', ['font_file','text_bg_box', 'fix_bounds']);
37 Schema::dropColumns('video_temp', ['bg_music', 'bgm_url']); 45 Schema::dropColumns('video_temp', ['bg_music', 'bgm_url']);
46 + Schema::dropColumns('admin_make_video', ['weather', 'huangli']);
38 } 47 }
39 } 48 }
......