Newer
Older
medialib-manager / static / js / media.js
function LSpinnerShow() {
    $(".loading-spinner-container").removeClass("d-none");
}

function LSpinnerHide() {
    $(".loading-spinner-container").addClass("d-none");
}

// Function to load media data
function loadMediaList() {
    LSpinnerShow();
    $("#media-table").addClass("d-none");

    const tableBody = $("#media-table tbody");
    tableBody.empty();

    $.ajax({
        url: "/media-list",
        method: "GET",
        success: function(data) {

            if (data.length > 0) {
                data.forEach((file, index) => {
                    const videoInfo = file.info.video
                        ? file.info.video.map((v, i) => !i ? `<li>Resolution: ${v.resolution}, Codec: ${v.codec}, Bitrate: ${v.bitrate}</li>` : `<li>Resolution: ${v.resolution}, Codec: ${v.codec}</li>`).join("")
                        : "<li>No video streams</li>";

                    const audioInfo = file.info.audio
                        ? file.info.audio.map(a => `<li>Channels: ${a.layout}, Codec: ${a.codec}, Bitrate: ${a.bitrate}, Language: ${a.language}</li>`).join("")
                        : "<li>No audio streams</li>";

                    const details = `
                        <b>Video:</b>
                        <ul>${videoInfo}</ul>
                        <b>Audio:</b>
                        <ul>${audioInfo}</ul>
                    `;

                    const pathEncoded = encodeURIComponent(file.path);

                    tableBody.append(`
                        <tr>
                            <td>${index + 1}</td>
                            <td>
                                <div class="filename"><a href="/single?path=${pathEncoded}"><b>${file.name}</b></a></div>
                                <div class="filepath">${file.path}</div>
                                <div class="filedetails">${details}</div>
                            </td>
                            <td class="size_unit_${file.size_unit}" data-order="${file.size_bytes}">${file.size}${file.size_unit}</td>
                        </tr>
                    `);
                });
            } else {
                tableBody.append(`<tr><td colspan="3" class="text-center">No files found</td></tr>`);
            }

            if(!$('#media-table_wrapper').length) {            
                $('#media-table').DataTable({
                    columnDefs: [
                        {
                            targets: "_all",
                            render: function(data, type, row, meta) {
                                var cell = meta.settings.aoData[meta.row].anCells[meta.col];
                                if (type === 'sort' && cell) {
                                    var orderValue = $(cell).attr('data-order');
                                    return orderValue !== undefined ? orderValue : data;
                                }
                                return data;
                            }
                        }
                    ]
                });
            }

            LSpinnerHide();
            $("#media-table").removeClass("d-none");
        },
        error: function(xhr, status, error) {
            LSpinnerHide();
            console.error("Error loading media list:", error);
        }
    });
}

function rescanMediaLibHandler() {
    $("#do-rescan-media-lib").on("click", function(){
        $.getJSON("/media-list/clear-cache").done(function(resp){
            if(resp.status) {
                return loadMediaList();
            }

            console.error("Error of rescan media library");
            // TODO: make alert bar
            alert("Error of rescan media library");
        });
    });
}

function transcodateFormHandlers() {
    const codecCRFMap = {
        "libx264": [0, 51],
        "libx265": [0, 51],
        "libvpx-vp9": [0, 63],
        "libaom-av1": [0, 63]
    };

    const maxResolution = $(`[name="resolution"]`).data("max-resolution");
    $(`[name="resolution"] option`).each(function(){
        parseInt($(this).attr("value")) > maxResolution 
            ? $(this).attr("disabled", "disabled") 
            : !$(`[name="resolution"] option[selected]`).length && $(this).attr("selected", "selected")
    });

    $(".show-transcodate-form").on("click", function(){
        const formContainer = $(".transcodate-form-container");
        if(formContainer.is(":visible")) {
            formContainer.slideUp();
        } else {
            formContainer.slideDown();
        }
    });

    $(".close-transcodate-form-container").on("click", function(){
        const formContainer = $(".transcodate-form-container");
        formContainer.slideUp();
    });

    const socket = io();

    // Listen for progress updates
    socket.on("progress", (data) => {
        document.getElementById("progress").innerText = data.message;
    });

    // Listen for completion
    socket.on("completed", (data) => {
        document.getElementById("progress").innerText = "Completed!";
    });

    // Listen for errors
    socket.on("error", (data) => {
        alert(data.message);
    });

    $(".transcodate-form-container .run-transcodate").on("click", async () => {
        const formContiner = $(".transcodate-form-container");
        const fields = formContiner.find("[name]");

        const data = {};
        for(let field of fields) {
            field = $(field);
            data[field.attr("name")] = field.val();
        }

        data["crf"] = "" + Math.abs(data["crf"]);
        console.log(data);

        const response = await fetch("/process-media", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(data)
        });

        if (response.ok) {
            document.getElementById("progress").innerText = "Processing started...";
        } else {
            const error = await response.json();
            alert(`Error: ${error.error}`);
        }

    });

    $("#codec").on("change", function(){
        const crf = $(`[name="crf"]`);

        const codec = $(this).val();
        crf.attr("min", codecCRFMap[codec][0]);
        crf.attr("max", codecCRFMap[codec][1]);
        if(parseInt(crf.val()) > codecCRFMap[codec][1]) {
            crf.val(codecCRFMap[codec][1]);
        }

        crf.parent().find(".crf-range").text(`${codecCRFMap[codec][0]}-${codecCRFMap[codec][1]}`);

        // toggle form of preset
        const presetLibxForm = $(`[name="preset-libx"]`);
        const presetVP9Form = $(`[name="preset-vp9"]`);
        const presetAV1Form = $(`[name="preset-av1"]`);
        if(codec == "libx264" || codec == "libx265") {
            presetLibxForm.show();
            presetVP9Form.hide();
            presetAV1Form.hide();
        } else if(codec == "libaom-av1"){
            presetLibxForm.hide();
            presetVP9Form.hide();
            presetAV1Form.show();
        } else if(codec == "libvpx-vp9") {
            presetLibxForm.hide();
            presetVP9Form.show();
            presetAV1Form.hide();
        }
    });

    $(`[name="crf"]`).on("input", function(){
        const val = parseInt($(this).val());

        if(isNaN(val)) {
            $(this).val(0);
            return;
        }

        if(val < 0) {
            $(this).val(0);
            return;
        }

        const max = parseInt($(this).attr("max"));
        if(val > max) {
            $(this).val(max);
        }
    });
}


// Load media list when the page is ready
$(document).ready(function() {
    loadMediaList();
    rescanMediaLibHandler();
    transcodateFormHandlers();
});