# [メタ情報] # 識別子: pCloud用再生URLおよびJSON_生成_exe # システム名: pCloud用再生URLおよびJSON_生成 # 技術種別: 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() { const ssId = '<固有のランダム文字列>'; const ss = SpreadsheetApp.openById(ssId); const sheetP = ss.getSheetByName('pcloudid'); const sheet1 = ss.getSheetByName('sheet1'); const authToken = PropertiesService.getScriptProperties().getProperty('PCLOUD_ACCESS_TOKEN'); if (!sheetP || !sheet1) return; const COL = { WPID: 1, FID: 2, NAME: 3, PATH: 4, URL: 5, UPD: 6, LAST: 7, PID: 8 }; // --- 1. 新規レコードの追加 --- const lastRow1 = sheet1.getLastRow(); if (lastRow1 >= 2) { const wpid1 = String(sheet1.getRange(2, 2).getValue()).trim(); const lastRowP = sheetP.getLastRow(); let alreadyExists = false; if (lastRowP >= 1) { const lastWpidP = String(sheetP.getRange(lastRowP, COL.WPID).getValue()).trim(); if (lastWpidP === wpid1) alreadyExists = true; } if (!alreadyExists && wpid1 !== "" && wpid1.toLowerCase() !== "wpidex") { const rawPath = String(sheet1.getRange(2, 6).getValue()); const filename = String(sheet1.getRange(2, 7).getValue()); const cleanPath = extractCleanPathSafe_(rawPath); sheetP.appendRow([ wpid1, "", filename, cleanPath, "", true, new Date(), "" ]); Logger.log('➡️ 追加完了: ' + wpid1); } } // --- 2. 処理 (F列がTRUEの行を更新) --- const dataP = sheetP.getDataRange().getValues(); for (let i = 1; i < dataP.length; i++) { const rowNum = i + 1; const needsUpdate = dataP[i][COL.UPD - 1]; if (needsUpdate === true || String(needsUpdate).toUpperCase() === "TRUE") { const currentWpid = String(dataP[i][COL.WPID - 1]).trim(); const rawDbPath = String(dataP[i][COL.PATH - 1]).trim(); const queryPath = extractCleanPathSafe_(rawDbPath); const pPath = '/Public Folder' + queryPath; Logger.log('🔄 照会: ' + pPath); try { // 【復刻】GETではなくPOSTで、NFCとNFDの両方を安全に問い合わせる const json = getPcloudStatSafe_(pPath, authToken); if (json.result === 0) { const fid = String(json.metadata.fileid); // ★ getPcloudStatSafe_ で記録した「通信に成功したパス」を使用する const actualPath = json._successfulPath.replace('/Public Folder', ''); const directUrl = 'https://filedn.eu/[pCloudの固有ID]' + 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); Logger.log('✅ 調理完了: ' + currentWpid); } else { Logger.log('⚠️ pCloud未検出(Result ' + json.result + '): ' + pPath); } } catch (e) { Logger.log('❌ 通信エラー: ' + e.message); } } } } /** * 【2月の核心ロジック復刻】 * URLエンコードによる破壊(Result 2055)を防ぐため POST を使用し、 * MacのNFD(分離文字)とNFC(標準文字)の両方でファイルを探す。 */ function getPcloudStatSafe_(path, authToken) { // 1. まず標準的な NFC(結合済み文字) で問い合わせ 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; // ★成功したパス(NFC)を記録 return jsonNfc; } // 2. NFCでダメなら、Mac特有の NFD(分離文字) で問い合わせ 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; // ★成功したパス(NFD)を記録 return jsonNfd; } // どちらもエラーの場合はNFC側の結果を返す return jsonNfc; } /** * パス抽出関数(安全版) */ function extractCleanPathSafe_(str) { var input = "" + str; var parts = input.split(','); var target = parts; 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; } ```