ken_nogi/NodeJS/ZoomGetRecordingData/getRecordingFiles.js
Kenichiro NOGI 48d993652f 20251006
2025-10-06 09:23:38 +09:00

347 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//
// ZOOM APIを使って録画データをダウンロードし、オリジナルデータは削除するスクリプト
// 2024-08-25 作成
//
// 登録されているユーザーのメールアドレスのフォルダを作り、そのなかにTopic名のフォルダを作成して、
// そこに個別にDLされる。ファイル名は録画開始日時とTOPIC名。
//
//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//Client ID : 3d605ODWTiKbJRrifQjerA
//Client Secret : HjxpKnITWdWQYgAajW8iWgq3YAZ61aLg
//Account ID : tpo4aQgXRdedqBGApzPTcA
//Verification Toke : CrWw9OrNQYm1QQ8telPNpw
const moment = require('moment-timezone');
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const clientId = '3d605ODWTiKbJRrifQjerA'
const clientSecret = 'HjxpKnITWdWQYgAajW8iWgq3YAZ61aLg';
const accountId = 'tpo4aQgXRdedqBGApzPTcA';
//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//実行する基準の月と月数を指定
//const baseYear = 2024;
//const baseMonth = 8
//const baseDate = moment(baseYear + '-' + baseMonth.toString().padStart(2, '0') + '-01T00:00:00Z').utc();
const monthLength = 1;
//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//Zoom APIのエンドポイント
const baseUrl = 'https://api.zoom.us/v2';
const baseDirectory = 'ZoomRecordings';
let accessToken;
let saveDirectory;
let fromDate;
let toDate;
//アクセストークンの取得
const getAccessToken = async () => {
try {
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
const response = await axios.post('https://zoom.us/oauth/token', null, {
headers: {
'Authorization': `Basic ${auth}`,
},
params: {
grant_type: 'account_credentials',
account_id: accountId
}
});
return response.data.access_token;
} catch (error) {
console.error('エラー アクセストークンの取得に失敗しました : ', error);
}
};
//ユーザー一覧取得
async function getUserList() {
const response = await axios.get(baseUrl + '/users', {
headers: {
'Authorization': `Bearer ${accessToken}`
},
params: {
//取得したいパラメータを指定 (e.g., type, status)
}
});
return response.data;
}
//ミーティングの一覧を取得
//UserIdを指定する
async function getMeetingList(userId) {
const response = await axios.get(baseUrl + '/users/' + userId + '/meetings', {
headers: {
'Authorization': `Bearer ${accessToken}`
},
params: {
//取得したいパラメータを指定 (e.g., type, status)
}
});
return response.data;
}
//ファイルリストの取得
async function getRecordingFileList(uuid) {
//ダウンロードリクエストを送信
const response = await axios.get(baseUrl + '/meetings/' + uuid + '/recordings', {
headers: {
'Authorization': `Bearer ${accessToken}`
},
params: {
//取得したいパラメータを指定 (e.g., type, status)
}
});
return response.data;
}
//録画リストの一覧を取得
//UserIdを指定する
async function getRecordingList(userId) {
//ダウンロードリクエストを送信
console.log(`▶▶▶ ${fromDate} ${toDate} の録画リストを取得中...`);
const response = await axios.get(baseUrl + '/users/' + userId + '/recordings', {
headers: {
'Authorization': `Bearer ${accessToken}`
},
params: {
//期間を指定
from: fromDate,
to: toDate
//取得したいパラメータを指定 (e.g., type, status)
}
});
return response.data;
}
//録画データをダウンロード
async function downloadFile(file, downloadUrl, filePath) {
console.log(`▶▶▶ ${downloadUrl} ダウンロード開始`);
try {
const response = await axios.get(downloadUrl, { responseType: 'stream' });
return new Promise((resolve, reject) => {
response.data.pipe(fs.createWriteStream(filePath))
.on('finish', async () => {
//ダウンロードできたら、ファイルを削除
await deleteRecording(file.meeting_id, file.id);
resolve();
})
.on('error', reject);
});
} catch (error) {
console.error(`Error downloading ${filePath}:`, error);
throw error;
}
};
//録画データを削除
async function deleteRecording(meetingUuid, recordingId) {
//ヘッダーの設定
const headers = {
'Authorization': `Bearer ${accessToken}`
};
//二重エンコードする
const encodedMeetingUuid = doubleEncodeUUID(meetingUuid);
//削除リクエストのURLを作成
const deleteUrl = `https://api.zoom.us/v2/meetings/${encodedMeetingUuid}/recordings/${recordingId}`;
try {
await axios.delete(deleteUrl, { headers });
console.log(`▶▶▶ 録画データ ${recordingId} を削除しました`);
} catch (error) {
console.error('Error deleting recording:', error);
}
}
async function recordingFileDownload(recordingList) {
for (meeting of recordingList.meetings) {
let startTime = meeting.start_time;
startTime = startTime.replaceAll(':', '.');
startTime = startTime.replaceAll('Z', '');
startTime = startTime.replaceAll('T', ' ');
let topicName = sanitizeFolderName(meeting.topic);
let saveFolder = startTime + ' ' + topicName + ' ' + meeting.id;
//フォルダが存在しない場合に作成
fs.mkdir(path.join(saveDirectory, saveFolder), { recursive: true }, (err) => {
if (err) {
console.error('Error creating directory:', err);
} else {
//console.log('Directory created successfully!');
}
});
console.log('▶▶▶ ' + saveFolder + ' 録画データのダウンロード開始');
for (file of meeting.recording_files) {
let startTime = file.recording_start;
startTime = startTime.replaceAll(':', '.');
startTime = startTime.replaceAll('Z', '');
startTime = startTime.replaceAll('T', ' ');
let downloadUrl = file.download_url + '?access_token=' + accessToken;
let fileName = startTime + ' ' + topicName + ' ' + meeting.id + '.' + file.file_extension;
let filePath = path.join(saveDirectory, saveFolder + '/' + fileName);
//console.log(file);
console.log("▶▶▶ " + fileName + ' ダウンロード開始');
await downloadFile(file, downloadUrl, filePath);
}
console.log('▷▷▷ ' + saveFolder + ' 録画データのダウンロード完了');
}
}
//メイン処理
async function main() {
let baseYear;
let baseMonth;
if (process.argv.length == 4) {
baseYear = process.argv[2];
baseMonth = process.argv[3];
if (isNaN(baseYear) || isNaN(baseMonth)) {
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
console.log("■ 引数が数字ではありません 実行例↓");
console.log("■ node .\\実行ファイル名 年 月");
console.log("■ 年と月は数字のみ(前0不要)");
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
return false;
} else {
baseYear = parseFloat(baseYear);
baseMonth = parseFloat(baseMonth);
if (baseYear <= 2000 || baseYear > 3000) {
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
console.log("■ 引数が数字ではありません 実行例↓");
console.log("■ node .\\実行ファイル名 年 月");
console.log("■ 年は西暦4桁で指定してください");
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
return false;
}
if (baseMonth < 1 || baseMonth > 12) {
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
console.log("■ 引数が数字ではありません 実行例↓");
console.log("■ node .\\実行ファイル名 年 月");
console.log("■ 月は112で指定してください");
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
return false;
}
}
} else {
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
console.log("■ 引数を正確に指定してください 実行例↓");
console.log("■ node .\\実行ファイル名 年 月");
console.log("■ 年と月は数字のみ(前0不要)");
console.log("■■■■■■■■■■■■■■■■■■■■■■■■■■");
return false;
}
let baseDate = moment(baseYear + '-' + baseMonth.toString().padStart(2, '0') + '-01T00:00:00Z').utc();
let fromDate0 = baseDate.clone();
let toDate0 = baseDate.clone().add(1, 'months').subtract(1, 'days');
//let toDate0 = baseDate.clone().add(1, 'months');
console.log("★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★");
console.log("★ ZOOM録画データのダウンロード開始");
console.log("■baseDate : " + baseDate);
//console.log("■ 検証のため停止");
//return false;
for (i = 0; i < monthLength; i++) {
fromDate = fromDate0.format('YYYY-MM-DD');;
toDate = toDate0.format('YYYY-MM-DD');;
console.log(fromDate + ' ' + toDate);
try {
//アクセストークンの取得
accessToken = await getAccessToken();
//console.log(accessToken);
//ユーザー情報一覧取得
const userList = await getUserList();
//console.log(userList);
//ユーザーIDを指定してミーティングリストの取得
//const meetingList = await getMeetingList('qQ0Tr6EHTBiYxOkSSd_CUw');
//console.log(meetingList);
//ミーティングIDを指定してファイルリストの取得
//const fileList = await getRecordingFileList('tmqmRfofTYSjU2AvjR7j/A==');
//console.log(fileList);
for (user of userList.users) {
saveDirectory = baseDirectory;
saveDirectory = path.join(saveDirectory, user.email);
let recordingList;
//録画データがなくなるまで繰り返す
do {
recordingList = await getRecordingList(user.id);
if (recordingList.meetings.length > 0) {
console.log("■■■ " + user.id + ' ' + user.display_name + ' ' + user.email);
console.log("▶▶▶ 録画データ数 :" + recordingList.meetings.length);
console.log(recordingList.meetings);
await recordingFileDownload(recordingList);
}
} while (recordingList.meetings.length > 0);
}
//録画リストの取得
//UserIDを指定する
//const recordingList = await getRecordingList('E4UdyiFuRvuvm_I4K-WqLQ');
//console.log(recordingList);
//取得した録画リストをダウンロードして削除する
//await recordingFileDownload(recordingList);
} catch (error) {
console.error(error);
}
//月を1つ進める
fromDate0 = fromDate0.clone().add(1, 'months');
toDate0 = toDate0.clone().add(1, 'months');
}
console.log("★ ZOOM録画データのダウンロード終了");
console.log("★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★");
}
//URLエンコードを2回行う
function doubleEncodeUUID(uuid) {
return encodeURIComponent(encodeURIComponent(uuid));
}
//フォルダ名に使えない文字列の排除
function sanitizeFolderName(str) {
// フォルダ名に使用できない一般的な文字の正規表現
const invalidCharsRegex = /[/\\:*?"<>|]/g;
// 空白をハイフンに置換
const sanitizedStr = str.replace(/\s+/g, '-');
// 不適切な文字を削除
return sanitizedStr.replace(invalidCharsRegex, '');
}
//処理開始
main();