Laravel中使用Ajax保存视频播放进度失败问题求助
Alright, let's break down what's wrong with your current code, explain how the saveTime route and controller method are supposed to work, and get your playback progress saving functionality up and running.
First: Identifying the Bugs in Your Code
There are several small but critical issues causing your code to fail:
Route Parameter Mismatch
Your route defines a parameter{video}, but your controller method expects$lession_id. This mismatch means Laravel can't correctly map the route parameter to your controller variable, leading to a 404 or incorrect lesson lookup.Incorrect Response Format
In your controller, you're passing the status code as part of the JSON array (e.g.,response()->json(['message' => 'User not authenticated', 404])). The correct syntax is to pass the status code as a second argument toresponse()->json(). Also, unauthenticated requests should return a401status code, not404.Wrong Data Type on User Update
You're trying to save the raw$watch_history_arrayto thewatch_historyfield, but it should be a JSON string. Your code creates$data['watch_history'] = json_encode(...)but doesn't actually use it in the update.Missing Parameter in Ajax Function Call
You callperformaction(v.currentTime, v.duration)but the function expects a thirdlession_idparameter. This would throw an undefined variable error in your JavaScript.Redundant JSON Parsing in Ajax Success
Since you setdataType: "json", jQuery automatically parses the response into a JavaScript object. CallingJSON.parse(data)again will throw an error.
How the saveTime Route & Controller Method Work
Let's clarify the intended logic:
Route Logic
Route::post('/saveTime/{lession_id}', 'InstructorCourseController@saveTime')->name('video.saveTime');
This is a POST route that accepts a lession_id parameter (the ID of the video lesson being watched). It maps to the saveTime method in InstructorCourseController, and has a named route video.saveTime for easy URL generation.
Controller Method Logic
The saveTime method is responsible for:
- Authenticating the User: Checks if the user is logged in (via
Auth::user()). If not, returns an error response. - Fetching the Lesson: Looks up the lesson using the provided
lession_idto validate it exists. - Handling Watch History:
- If the user has no existing watch history, initializes an array and adds the current lesson's progress.
- If history exists, decodes the JSON string into an array, checks if the lesson is already in the history:
- If found: Updates the progress to the current playback time.
- If not found: Adds a new entry for the lesson.
- Saving the History: Converts the updated array back to a JSON string and saves it to the user's
watch_historyfield. - Returning a Response: Sends a JSON success (or error) response back to the Ajax call.
Fixed Working Code
Here's the corrected code for all components:
1. Updated Route
// 展示课程视频的路由 Route::get('/lession_video/{lession_id}','FrontendCommonController@lession_video')->name('lession_video'); // 用于Ajax保存视频播放进度的路由(修正参数名) Route::post('/saveTime/{lession_id}', 'InstructorCourseController@saveTime')->name('video.saveTime');
2. Fixed Controller Method
use Illuminate\Support\Facades\Auth; use App\User; use App\Lession; public function saveTime(Request $request, $lession_id){ $user = Auth::user(); if($user === null){ // 未认证返回401状态码 return response()->json(['message' => 'User not authenticated'], 401); } // 验证课程是否存在 $video = Lession::find($lession_id); if(!$video){ return response()->json(['message' => 'Lesson not found'], 404); } $currentTime = $request->input('time'); $duration = $request->input('duration'); // 初始化观看历史数组 $watch_history_array = []; if (!empty($user->watch_history)) { $watch_history_array = json_decode($user->watch_history, true); // 确保解码后是有效数组(防止损坏的JSON) if(!is_array($watch_history_array)){ $watch_history_array = []; } } // 查找是否已有当前课程的记录 $foundIndex = -1; foreach($watch_history_array as $index => $history){ if($history['lesson_id'] == $lession_id){ $foundIndex = $index; break; } } if($foundIndex !== -1){ // 更新现有记录的进度 $watch_history_array[$foundIndex]['progress'] = $currentTime; } else { // 添加新的观看记录 $watch_history_array[] = [ 'lesson_id' => $lession_id, 'progress' => $currentTime ]; } // 将数组转为JSON字符串并更新用户 $user->update(['watch_history' => json_encode($watch_history_array)]); return response()->json(['message' => 'Time saved successfully'], 200); }
3. Improved Blade Video & Ajax Code
<video id="player" poster="/public/images/{{$lession->thumbnail}}" class="plyr"> <source src="/public/images/{{$lession->resource}}" /> </video> <script> var vid = document.getElementById("player"); var lessionId = {{ $lession->id }}; // 存储当前课程ID $(function() { var timeout; $("#player").on("playing pause", function(e) { var v = this; clearTimeout(timeout); // 传递课程ID给performaction函数 performaction(v.currentTime, v.duration, lessionId); if (e.type === "playing") { // 每1秒保存一次进度 timeout = setInterval(function() { performaction(v.currentTime, v.duration, lessionId); }, 1000); } }); function performaction(currentTime, duration, lession_id) { var data = { _token: '{{ csrf_token() }}', // 直接把CSRF令牌放在请求数据中 time: currentTime, duration: duration }; $.ajax({ type: "POST", // 替换路由中的参数占位符 url: '{{ route("video.saveTime", ":lession_id") }}'.replace(':lession_id', lession_id), data: data, dataType: "json", success: function(response) { console.log('Progress saved:', response.message); // 可选:如果需要用户提示,取消注释下面的alert // alert(response.message); }, error: function(xhr) { var error = xhr.responseJSON; console.error('Save failed:', error.message); // alert(error.message); } }); } }); </script>
Bonus: Optimize with Laravel Model Casting
To simplify your code further, you can use Laravel's model casting to automatically handle JSON serialization for the watch_history field. Add this to your User model:
protected $casts = [ 'watch_history' => 'array', ];
With this, you can directly work with arrays in your controller (no need for json_encode/json_decode):
// 初始化数组(自动从JSON转换) $watch_history_array = $user->watch_history ?? []; // 更新后直接保存数组(Laravel自动转为JSON) $user->update(['watch_history' => $watch_history_array]);
内容的提问来源于stack exchange,提问作者Abhishek pardeshi

