You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中使用Ajax保存视频播放进度失败问题求助

Fixing Video Playback Progress Saving in Laravel with 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:

  1. 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.

  2. 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 to response()->json(). Also, unauthenticated requests should return a 401 status code, not 404.

  3. Wrong Data Type on User Update
    You're trying to save the raw $watch_history_array to the watch_history field, but it should be a JSON string. Your code creates $data['watch_history'] = json_encode(...) but doesn't actually use it in the update.

  4. Missing Parameter in Ajax Function Call
    You call performaction(v.currentTime, v.duration) but the function expects a third lession_id parameter. This would throw an undefined variable error in your JavaScript.

  5. Redundant JSON Parsing in Ajax Success
    Since you set dataType: "json", jQuery automatically parses the response into a JavaScript object. Calling JSON.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:

  1. Authenticating the User: Checks if the user is logged in (via Auth::user()). If not, returns an error response.
  2. Fetching the Lesson: Looks up the lesson using the provided lession_id to validate it exists.
  3. 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.
  4. Saving the History: Converts the updated array back to a JSON string and saves it to the user's watch_history field.
  5. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 14:22:32