# [メタ情報] # 識別子: pCloud用再生URLおよびJSON_生成_exe # システム名: pCloud用再生URLおよびJSON_生成 env終了 # 技術種別: Misc # 機能名: Misc # 使用言語: GAS # 状態: 公開用 # [/メタ情報] 要約: このGoogle Apps Script (`updatePcloudLibrary`) は、スプレッドシートのデータを基にpCloudライブラリの情報を管理・更新します。 スクリプトは主に二つの処理を行います。 1. **新規レコードの追加**: `sheet1`から新しい記事ID (WPID) を取得し、`pcloudid`シートに未登録であれば、ファイル名とクリーンなパス情報と共に新規行を追加します。 2. **既存レコードの更新**: `pcloudid`シート内の「更新が必要」(UPD列がTRUE)とマークされた各行を処理します。`getPcloudStatSafe_`関数を用いて、対象パスのpCloud上のファイル情報を取得します。この関数は、POSTリクエストとNFC/NFD両方の文字正規化を試みることで、ファイル検索の確実性を高めています。取得に成功した場合、pCloud上のファイルID、正確なファイルパス、および直接アクセスURLを`pcloudid`シートに書き込み、更新フラグをFALSEに設定します。ファイルが見つからない場合やAPI通信エラーが発生した場合は、ログに記録されます。 補助関数として、`extractCleanPathSafe_`がパス文字列から不要な部分を削除し、適切なファイルパスを抽出します。主な目的は、スプレッドシート上のファイル情報とpCloud上の実際のファイル情報を同期させ、アクセス可能なURLを生成することです。 updatePcloudLibrary.gs ``` function updatePcloudLibrary() { // ===== 1. 環境設定(GASスクリプトプロパティからの動的ロード & フォールバック完全排除) ===== const ssId = PropertiesService.getScriptProperties().getProperty('F1_SPREADSHEET_ID'); const authToken = PropertiesService.getScriptProperties().getProperty('PCLOUD_ACCESS_TOKEN'); const pcloudBaseUrl = PropertiesService.getScriptProperties().getProperty('PCLOUD_BASE_URL'); // ===== 安全な起動時自律停止ガード ===== if (!ssId || !authToken || !pcloudBaseUrl) { Logger.log('❌ エラー: 必要なスクリプトプロパティ (F1_SPREADSHEET_ID, PCLOUD_ACCESS_TOKEN, PCLOUD_BASE_URL) が設定されていません。'); return; } const ss = SpreadsheetApp.openById(ssId); const sheetP = ss.getSheetByName('pcloudid'); const sheet1 = ss.getSheetByName('sheet1'); if (!sheetP || !sheet1) { Logger.log('❌ エラー: 対象シート (pcloudid または sheet1) が存在しません。'); return; } const COL = { WPID: 1, FID: 2, NAME: 3, PATH: 4, URL: 5, UPD: 6, LAST: 7, PID: 8 }; // --- F列(UPD)がTRUEの行を更新 --- const lastRowP = sheetP.getLastRow(); if (lastRowP < 2) return; const dataP = sheetP.getRange(2, 1, lastRowP - 1, 8).getValues(); for (let i = 0; i < dataP.length; i++) { const rowNum = i + 2; const currentWpid = String(dataP[i][COL.WPID - 1]).trim(); const rawDbPath = String(dataP[i][COL.PATH - 1]).trim(); const needsUpdate = dataP[i][COL.UPD - 1]; if (needsUpdate === true && rawDbPath !== "") { const queryPath = extractCleanPathSafe_(rawDbPath); const pPath = '/Public Folder' + queryPath; Logger.log('🔄 照会: ' + pPath); try { const json = getPcloudStatSafe_(pPath, authToken); if (json.result === 0) { const fid = String(json.metadata.fileid); const actualPath = json._successfulPath.replace('/Public Folder', ''); const baseUrlClean = pcloudBaseUrl.endsWith('/') ? pcloudBaseUrl.slice(0, -1) : pcloudBaseUrl; const directUrl = baseUrlClean + encodeURI(actualPath); sheetP.getRange(rowNum, COL.FID).setValue(fid); sheetP.getRange(rowNum, COL.PATH).setValue(actualPath); sheetP.getRange(rowNum, COL.URL).setValue(directUrl); sheetP.getRange(rowNum, COL.UPD).setValue(false); sheetP.getRange(rowNum, COL.PID).setValue(fid); sheetP.getRange(rowNum, COL.LAST).setValue(new Date()); Logger.log('✅ 調理完了: ' + currentWpid); } else { Logger.log('⚠️ pCloud未検出(Result ' + json.result + '): ' + pPath); } } catch (e) { Logger.log('❌ 通信エラー: ' + e.message); } } } } /** * URLエンコードによる破壊(Result 2055)を防ぐため POST を使用し、 * MacのNFD(分離文字)とNFC(標準文字)の両方でファイルを探す。 */ function getPcloudStatSafe_(path, authToken) { var nfcPath = path.normalize('NFC'); var resNfc = UrlFetchApp.fetch('https://eapi.pcloud.com/stat', { method: 'post', payload: { path: nfcPath, auth: authToken }, muteHttpExceptions: true }); var jsonNfc = JSON.parse(resNfc.getContentText()); if (jsonNfc.result === 0) { jsonNfc._successfulPath = nfcPath; return jsonNfc; } var nfdPath = path.normalize('NFD'); var resNfd = UrlFetchApp.fetch('https://eapi.pcloud.com/stat', { method: 'post', payload: { path: nfdPath, auth: authToken }, muteHttpExceptions: true }); var jsonNfd = JSON.parse(resNfd.getContentText()); if (jsonNfd.result === 0) { jsonNfd._successfulPath = nfdPath; return jsonNfd; } return jsonNfc; } /** * パス抽出関数(安全版) */ function extractCleanPathSafe_(str) { var input = "" + str; var parts = input.split(','); var target = parts[0]; var finalStr = String(target).trim(); var markers = ['pmedia/', 'mmedia/']; for (var i = 0; i < markers.length; i++) { var m = markers[i]; var pos = finalStr.indexOf(m); if (pos !== -1) { return '/' + finalStr.substring(pos); } } return finalStr.startsWith('/') ? finalStr : '/' + finalStr; } ```