Created
August 5, 2026 02:22
-
-
Save ochilab/7c38d40a7206cff9d5aa187f1a2b3ecc to your computer and use it in GitHub Desktop.
フォームで収集したスライドと動画の親フォルダを取得する
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 現在開いているGoogleシートのB列にある対象シートURLを順に処理し、 | |
| * C列にスライド親フォルダURL、 | |
| * D列に動画親フォルダURLを書き込む。 | |
| * | |
| * 一覧シート: | |
| * A列: 任意 | |
| * B列: 対象GoogleシートURL | |
| * C列: スライド親フォルダURL | |
| * D列: 動画親フォルダURL | |
| */ | |
| function writeParentFolderUrls() { | |
| const config = { | |
| startRow: 2, | |
| // 対象Googleシート内の設定 | |
| targetSheetName: '', // 空欄なら先頭シートを使用 | |
| headerRow: 1, | |
| // 対象シート内の見出し名 | |
| slideUrlHeader: 'スライド', | |
| videoUrlHeader: '動画' | |
| }; | |
| const activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet(); | |
| const listSheet = activeSpreadsheet.getActiveSheet(); | |
| const lastRow = listSheet.getLastRow(); | |
| if (lastRow < config.startRow) { | |
| return; | |
| } | |
| const numberOfRows = lastRow - config.startRow + 1; | |
| // 一覧シートB列のGoogleシートURL | |
| const spreadsheetUrls = listSheet | |
| .getRange(config.startRow, 2, numberOfRows, 1) | |
| .getDisplayValues(); | |
| const results = spreadsheetUrls.map(([spreadsheetUrl]) => { | |
| if (!spreadsheetUrl.trim()) { | |
| return ['', '']; | |
| } | |
| try { | |
| const result = getFolderUrlsFromSpreadsheet_( | |
| spreadsheetUrl, | |
| config | |
| ); | |
| return [ | |
| result.slideParentFolderUrl, | |
| result.videoParentFolderUrl | |
| ]; | |
| } catch (error) { | |
| console.error( | |
| `処理失敗: ${spreadsheetUrl}: ${error.message}` | |
| ); | |
| return [ | |
| `エラー: ${error.message}`, | |
| `エラー: ${error.message}` | |
| ]; | |
| } | |
| }); | |
| // C列・D列へ一括出力 | |
| listSheet | |
| .getRange(config.startRow, 3, results.length, 2) | |
| .setValues(results); | |
| // 見出しを必要に応じて設定 | |
| if (!listSheet.getRange(1, 3).getValue()) { | |
| listSheet.getRange(1, 3).setValue('スライド親フォルダURL'); | |
| } | |
| if (!listSheet.getRange(1, 4).getValue()) { | |
| listSheet.getRange(1, 4).setValue('動画親フォルダURL'); | |
| } | |
| } | |
| /** | |
| * 指定されたGoogleシートからスライドURL・動画URLを探し、 | |
| * それぞれの親フォルダURLを取得する。 | |
| */ | |
| function getFolderUrlsFromSpreadsheet_(spreadsheetUrl, config) { | |
| const spreadsheet = SpreadsheetApp.openByUrl(spreadsheetUrl); | |
| const sheet = config.targetSheetName | |
| ? spreadsheet.getSheetByName(config.targetSheetName) | |
| : spreadsheet.getSheets()[0]; | |
| if (!sheet) { | |
| throw new Error('対象シートが見つかりません'); | |
| } | |
| const lastRow = sheet.getLastRow(); | |
| const lastColumn = sheet.getLastColumn(); | |
| if (lastRow <= config.headerRow) { | |
| throw new Error('対象シートにデータがありません'); | |
| } | |
| const headers = sheet | |
| .getRange(config.headerRow, 1, 1, lastColumn) | |
| .getDisplayValues()[0] | |
| .map(value => value.trim()); | |
| const slideColumn = findColumnByHeader_( | |
| headers, | |
| config.slideUrlHeader | |
| ); | |
| const videoColumn = findColumnByHeader_( | |
| headers, | |
| config.videoUrlHeader | |
| ); | |
| const slideUrl = getFirstUrlFromColumn_( | |
| sheet, | |
| slideColumn, | |
| config.headerRow + 1, | |
| lastRow | |
| ); | |
| const videoUrl = getFirstUrlFromColumn_( | |
| sheet, | |
| videoColumn, | |
| config.headerRow + 1, | |
| lastRow | |
| ); | |
| return { | |
| slideParentFolderUrl: slideUrl | |
| ? getParentFolderUrl_(slideUrl) | |
| : '', | |
| videoParentFolderUrl: videoUrl | |
| ? getParentFolderUrl_(videoUrl) | |
| : '' | |
| }; | |
| } | |
| /** | |
| * 指定列から最初の有効なURLを取得する。 | |
| * | |
| * Googleフォームのファイルアップロード回答では、 | |
| * セルの表示値ではなくリッチテキストのリンクとして | |
| * URLが格納される場合もあるため、両方に対応する。 | |
| */ | |
| function getFirstUrlFromColumn_( | |
| sheet, | |
| column, | |
| startRow, | |
| lastRow | |
| ) { | |
| const numberOfRows = lastRow - startRow + 1; | |
| const range = sheet.getRange( | |
| startRow, | |
| column, | |
| numberOfRows, | |
| 1 | |
| ); | |
| const displayValues = range.getDisplayValues(); | |
| const richTextValues = range.getRichTextValues(); | |
| for (let i = 0; i < numberOfRows; i++) { | |
| const richText = richTextValues[i][0]; | |
| if (richText) { | |
| const directLink = richText.getLinkUrl(); | |
| if (directLink) { | |
| return directLink; | |
| } | |
| const runs = richText.getRuns(); | |
| for (const run of runs) { | |
| const link = run.getLinkUrl(); | |
| if (link) { | |
| return link; | |
| } | |
| } | |
| } | |
| const text = displayValues[i][0].trim(); | |
| if (text) { | |
| const url = extractFirstUrlFromText_(text); | |
| if (url) { | |
| return url; | |
| } | |
| } | |
| } | |
| return ''; | |
| } | |
| /** | |
| * ファイルURLから親フォルダURLを取得する。 | |
| */ | |
| function getParentFolderUrl_(fileUrl) { | |
| const fileId = extractDriveId_(fileUrl); | |
| if (!fileId) { | |
| throw new Error(`Drive IDを抽出できません: ${fileUrl}`); | |
| } | |
| const file = DriveApp.getFileById(fileId); | |
| const parents = file.getParents(); | |
| if (!parents.hasNext()) { | |
| throw new Error(`親フォルダがありません: ${fileUrl}`); | |
| } | |
| return parents.next().getUrl(); | |
| } | |
| /** | |
| * 見出し名から列番号を取得する。 | |
| */ | |
| function findColumnByHeader_(headers, headerName) { | |
| const normalizedTarget = normalizeHeader_(headerName); | |
| const index = headers.findIndex(header => { | |
| return normalizeHeader_(header) === normalizedTarget; | |
| }); | |
| if (index === -1) { | |
| throw new Error( | |
| `見出し「${headerName}」が見つかりません` | |
| ); | |
| } | |
| return index + 1; | |
| } | |
| /** | |
| * 見出し比較用の正規化。 | |
| */ | |
| function normalizeHeader_(text) { | |
| return String(text) | |
| .trim() | |
| .replace(/\s+/g, '') | |
| .toLowerCase(); | |
| } | |
| /** | |
| * 文字列に含まれる最初のURLを取得する。 | |
| */ | |
| function extractFirstUrlFromText_(text) { | |
| const match = String(text).match(/https?:\/\/[^\s,]+/); | |
| return match ? match[0] : ''; | |
| } | |
| /** | |
| * Google Drive関連URLからファイルIDを抽出する。 | |
| */ | |
| function extractDriveId_(url) { | |
| const text = String(url).trim(); | |
| const patterns = [ | |
| /\/d\/([a-zA-Z0-9_-]+)/, | |
| /\/file\/d\/([a-zA-Z0-9_-]+)/, | |
| /[?&]id=([a-zA-Z0-9_-]+)/, | |
| /^([a-zA-Z0-9_-]{20,})$/ | |
| ]; | |
| for (const pattern of patterns) { | |
| const match = text.match(pattern); | |
| if (match) { | |
| return match[1]; | |
| } | |
| } | |
| return ''; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment