428 lines
13 KiB
JavaScript
428 lines
13 KiB
JavaScript
jQuery(document).ready(function ($) {
|
|
let mediaRecorder;
|
|
let audioChunks = [];
|
|
let isRecording = false;
|
|
let audioContext, analyser, dataArray, bufferLength, source;
|
|
let isVisualizing = false;
|
|
|
|
function visualize(stream) {
|
|
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
analyser = audioContext.createAnalyser();
|
|
source = audioContext.createMediaStreamSource(stream);
|
|
source.connect(analyser);
|
|
analyser.fftSize = 2048;
|
|
bufferLength = analyser.frequencyBinCount;
|
|
dataArray = new Uint8Array(bufferLength);
|
|
|
|
const canvas = document.getElementById("visualizer");
|
|
const canvasCtx = canvas.getContext("2d");
|
|
|
|
function draw() {
|
|
if (!isVisualizing) return;
|
|
requestAnimationFrame(draw);
|
|
analyser.getByteTimeDomainData(dataArray);
|
|
|
|
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
const centerY = canvas.height / 2;
|
|
canvasCtx.beginPath();
|
|
canvasCtx.moveTo(0, centerY);
|
|
canvasCtx.lineTo(canvas.width, centerY);
|
|
canvasCtx.strokeStyle = "rgba(0, 0, 0, 0.2)";
|
|
canvasCtx.lineWidth = 1;
|
|
canvasCtx.stroke();
|
|
|
|
canvasCtx.lineWidth = 2;
|
|
canvasCtx.strokeStyle = "rgb(0, 0, 0)";
|
|
|
|
canvasCtx.beginPath();
|
|
let sliceWidth = (canvas.width * 1.0) / bufferLength;
|
|
let x = 0;
|
|
|
|
for (let i = 0; i < bufferLength; i++) {
|
|
let v = dataArray[i] / 128.0;
|
|
let y = (v * canvas.height) / 2;
|
|
|
|
if (i === 0) {
|
|
canvasCtx.moveTo(x, y);
|
|
} else {
|
|
canvasCtx.lineTo(x, y);
|
|
}
|
|
|
|
x += sliceWidth;
|
|
}
|
|
|
|
canvasCtx.lineTo(canvas.width, canvas.height / 2);
|
|
canvasCtx.stroke();
|
|
}
|
|
|
|
isVisualizing = true;
|
|
draw();
|
|
}
|
|
|
|
function showToast(message, type) {
|
|
$.toast({
|
|
text: message,
|
|
heading: "Note",
|
|
icon: type,
|
|
showHideTransition: "fade",
|
|
allowToastClose: true,
|
|
hideAfter: 3000,
|
|
stack: 3,
|
|
position: "bottom-center",
|
|
textAlign: "left",
|
|
loader: true,
|
|
loaderBg: type === "success" ? "#9EC600" : "#FF0000",
|
|
});
|
|
}
|
|
|
|
$(".download-single").on("click", function () {
|
|
var $button = $(this);
|
|
var fileUrl = $button.data("url");
|
|
var fileName = $button.data("name");
|
|
|
|
var downloadLink = document.createElement("a");
|
|
downloadLink.href = fileUrl;
|
|
downloadLink.download = fileName;
|
|
document.body.appendChild(downloadLink);
|
|
downloadLink.click();
|
|
document.body.removeChild(downloadLink);
|
|
});
|
|
|
|
$("#download-zip").on("click", function () {
|
|
var selectedFiles = [];
|
|
$(".select-audio:checked").each(function () {
|
|
selectedFiles.push($(this).val());
|
|
});
|
|
|
|
if (selectedFiles.length > 0) {
|
|
$.ajax({
|
|
url: ajaxurl,
|
|
type: "POST",
|
|
data: {
|
|
action: "download_zip",
|
|
files: selectedFiles,
|
|
},
|
|
success: function (response) {
|
|
if (response.success) {
|
|
showToast("Zip file created successfully", "success");
|
|
var downloadLink = document.createElement("a");
|
|
downloadLink.href = response.data.zip_url;
|
|
downloadLink.download = response.data.zip_url.split("/").pop();
|
|
document.body.appendChild(downloadLink);
|
|
downloadLink.click();
|
|
document.body.removeChild(downloadLink);
|
|
$(".select-audio").prop("checked", false);
|
|
} else {
|
|
showToast("Failed to create zip file: " + response.data, "error");
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
showToast("AJAX Error: " + textStatus + ": " + errorThrown, "error");
|
|
},
|
|
});
|
|
} else {
|
|
showToast("Please select at least one audio file to download.", "error");
|
|
}
|
|
});
|
|
|
|
$("#recording-button").on("click", function () {
|
|
if (!isRecording) {
|
|
navigator.mediaDevices
|
|
.getUserMedia({ audio: true })
|
|
.then((stream) => {
|
|
mediaRecorder = new MediaRecorder(stream);
|
|
audioChunks = [];
|
|
mediaRecorder.start();
|
|
visualize(stream);
|
|
|
|
mediaRecorder.ondataavailable = function (event) {
|
|
audioChunks.push(event.data);
|
|
};
|
|
|
|
mediaRecorder.onstop = function () {
|
|
stream.getTracks().forEach((track) => track.stop());
|
|
const audioBlob = new Blob(audioChunks, { type: "audio/wav" });
|
|
audioChunks = [];
|
|
const audioUrl = URL.createObjectURL(audioBlob);
|
|
$("#audio-player").attr("src", audioUrl).addClass("show");
|
|
|
|
let formData = new FormData();
|
|
formData.append("audio_data", audioBlob, "audio.wav");
|
|
formData.append("action", "save_audio");
|
|
|
|
$.ajax({
|
|
url: ajaxurl,
|
|
type: "POST",
|
|
data: formData,
|
|
processData: false,
|
|
contentType: false,
|
|
success: function (response) {
|
|
if (response.success) {
|
|
showToast("Audio saved successfully", "success");
|
|
} else {
|
|
showToast("Failed to save audio", "error");
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
$("#visualizer").show();
|
|
isRecording = true;
|
|
$("#recording-button").css("background-color", "green");
|
|
})
|
|
.catch((error) => {
|
|
showToast("Unable to access microphone: " + error.message, "error");
|
|
});
|
|
} else {
|
|
mediaRecorder.stop();
|
|
$("#visualizer").hide();
|
|
isVisualizing = false;
|
|
audioContext.close();
|
|
isRecording = false;
|
|
$("#recording-button").css("background-color", "red");
|
|
}
|
|
});
|
|
|
|
$("#download-selected").on("click", function () {
|
|
let selectedFiles = [];
|
|
$(".select-audio:checked").each(function () {
|
|
selectedFiles.push($(this).val());
|
|
});
|
|
|
|
if (selectedFiles.length > 0) {
|
|
$.ajax({
|
|
url: ajaxurl,
|
|
type: "POST",
|
|
data: {
|
|
action: "download_zip",
|
|
files: selectedFiles,
|
|
},
|
|
success: function (response) {
|
|
if (response.success) {
|
|
showToast("Zip file created successfully", "success");
|
|
let downloadLink = document.createElement("a");
|
|
downloadLink.href = response.data.zip_url;
|
|
downloadLink.download = response.data.zip_url.split("/").pop();
|
|
document.body.appendChild(downloadLink);
|
|
downloadLink.click();
|
|
document.body.removeChild(downloadLink);
|
|
$(".select-audio").prop("checked", false);
|
|
} else {
|
|
showToast("Failed to create zip file: " + response.data, "error");
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
showToast("AJAX Error: " + textStatus + ": " + errorThrown, "error");
|
|
},
|
|
});
|
|
}
|
|
});
|
|
|
|
$("#select-all").on("click", function () {
|
|
let isSelectAll = $(this).data("select-all");
|
|
if (isSelectAll) {
|
|
$(".select-audio").prop("checked", true);
|
|
$(this).text("Unselect All");
|
|
} else {
|
|
$(".select-audio").prop("checked", false);
|
|
$(this).text("Select All");
|
|
}
|
|
$(this).data("select-all", !isSelectAll);
|
|
});
|
|
|
|
// فقط یه رویداد برای #delete-selected
|
|
$("#delete-selected").on("click", function () {
|
|
const selectedFiles = [];
|
|
$(".select-audio:checked").each(function () {
|
|
selectedFiles.push($(this).val());
|
|
});
|
|
|
|
if (selectedFiles.length === 0) {
|
|
showToast("Please select at least one audio file to delete.", "error");
|
|
return;
|
|
}
|
|
|
|
const confirmDialog = confirm(
|
|
"Are you sure you want to delete " +
|
|
selectedFiles.length +
|
|
" selected file(s)?\nPress OK to delete only locally, or Cancel and choose below."
|
|
);
|
|
if (!confirmDialog) {
|
|
const deleteFromDrive = confirm(
|
|
"Also delete these files from Google Drive?"
|
|
);
|
|
deleteFiles(selectedFiles, deleteFromDrive);
|
|
} else {
|
|
deleteFiles(selectedFiles, false);
|
|
}
|
|
});
|
|
|
|
function deleteFiles(files, deleteFromDrive) {
|
|
$.ajax({
|
|
url: ajaxurl,
|
|
type: "POST",
|
|
data: {
|
|
action: "audio_diary_delete_selected_audios",
|
|
files: files,
|
|
delete_from_drive: deleteFromDrive,
|
|
},
|
|
success: function (response) {
|
|
if (response.success) {
|
|
showToast(
|
|
"Deleted " +
|
|
(response.deleted_local || 0) +
|
|
" local file(s) and " +
|
|
(response.deleted_drive || 0) +
|
|
" Google Drive file(s) successfully!",
|
|
"success"
|
|
);
|
|
files.forEach(function (fileName) {
|
|
const $row = $('tr[data-file="' + fileName + '"]');
|
|
$row
|
|
.find(".upload-to-drive")
|
|
.text("Upload to Drive")
|
|
.removeClass("uploaded")
|
|
.prop("disabled", false);
|
|
$row.fadeOut(400, function () {
|
|
$(this).remove();
|
|
});
|
|
});
|
|
if (response.data && response.data.drive_errors) {
|
|
showToast(response.data.drive_errors, "warning");
|
|
}
|
|
} else {
|
|
showToast(
|
|
"Failed to delete files: " + (response.data || "Unknown error"),
|
|
"error"
|
|
);
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
showToast("AJAX Error: " + textStatus + " - " + errorThrown, "error");
|
|
},
|
|
});
|
|
}
|
|
|
|
$(".upload-to-drive").on("click", function () {
|
|
const $button = $(this);
|
|
const fileName = $button.data("name");
|
|
|
|
if ($button.hasClass("uploaded")) {
|
|
showToast("This file is already uploaded to Google Drive.", "error");
|
|
return;
|
|
}
|
|
|
|
$button.text("Uploading...").prop("disabled", true);
|
|
|
|
$.ajax({
|
|
url: ajaxurl,
|
|
type: "POST",
|
|
data: {
|
|
action: "upload_to_google_drive",
|
|
file: fileName,
|
|
},
|
|
success: function (response) {
|
|
if (response.success) {
|
|
showToast("File uploaded to Google Drive successfully!", "success");
|
|
$button.text("Uploaded").addClass("uploaded").prop("disabled", true);
|
|
} else {
|
|
if (response.data === "File already exists in Google Drive.") {
|
|
showToast("This file already exists in Google Drive.", "error");
|
|
$button
|
|
.text("Uploaded")
|
|
.addClass("uploaded")
|
|
.prop("disabled", true);
|
|
} else {
|
|
showToast("Failed to upload: " + response.data, "error");
|
|
$button.text("Upload to Drive").prop("disabled", false);
|
|
}
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
showToast("Upload Error: " + textStatus, "error");
|
|
$button.text("Upload to Drive").prop("disabled", false);
|
|
},
|
|
});
|
|
});
|
|
|
|
$("button#audioduration").on("click", function () {
|
|
var $button = $(this);
|
|
if ($button.data("loaded")) return;
|
|
var audioUrl = $button.closest("td").find(".audio-duration").data("url");
|
|
|
|
if (!audioUrl) {
|
|
$button.text("خطا");
|
|
return;
|
|
}
|
|
|
|
$button.text("در حال بارگذاری...");
|
|
var audio = new Audio(audioUrl);
|
|
|
|
audio.addEventListener("canplaythrough", function () {
|
|
var duration = audio.duration;
|
|
|
|
if (isNaN(duration) || duration <= 0) {
|
|
$button.text("خطا");
|
|
return;
|
|
}
|
|
|
|
var minutes = Math.floor(duration / 60);
|
|
var seconds = Math.floor(duration % 60);
|
|
var formattedDuration =
|
|
minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
|
|
|
|
$button.text(formattedDuration);
|
|
$button.data("loaded", true);
|
|
});
|
|
|
|
audio.addEventListener("error", function () {
|
|
$button.text("خطا");
|
|
});
|
|
|
|
audio.load();
|
|
});
|
|
|
|
$("button.audioduration").on("click", function () {
|
|
var $button = $(this);
|
|
if ($button.data("loaded")) return;
|
|
|
|
var audioUrl = $button.closest("td").find(".audio-duration").data("url");
|
|
if (!audioUrl) {
|
|
$button.text("خطا");
|
|
return;
|
|
}
|
|
|
|
$button.text("در حال بارگذاری...");
|
|
var audio = new Audio(audioUrl);
|
|
|
|
audio.addEventListener("canplaythrough", function () {
|
|
var duration = audio.duration;
|
|
|
|
if (isNaN(duration) || duration <= 0) {
|
|
$button.text("خطا");
|
|
return;
|
|
}
|
|
|
|
var minutes = Math.floor(duration / 60);
|
|
var seconds = Math.floor(duration % 60);
|
|
var formattedDuration =
|
|
minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
|
|
|
|
$button.text(formattedDuration);
|
|
$button.data("loaded", true);
|
|
});
|
|
|
|
audio.addEventListener("error", function () {
|
|
$button.text("خطا");
|
|
});
|
|
|
|
audio.load();
|
|
});
|
|
|
|
$(".select-audio").on("change", function () {
|
|
let count = $(".select-audio:checked").length;
|
|
$("#selected-count").text(`Selected: ${count}`);
|
|
});
|
|
});
|