Skip to content

Instantly share code, notes, and snippets.

@kennak0
Last active July 17, 2025 16:21
Show Gist options
  • Select an option

  • Save kennak0/0658ec642c733b36a6d702c3aec8db23 to your computer and use it in GitHub Desktop.

Select an option

Save kennak0/0658ec642c733b36a6d702c3aec8db23 to your computer and use it in GitHub Desktop.
選択したテキストや画像を直接Tumblrに投稿(OAuth不要・ショートカットキー: Ctrl+Shift+P)
// ==UserScript==
// @name Tumblr Direct Post
// @namespace http://tampermonkey.net/
// @version 2.3
// @description 選択したテキストや画像を直接Tumblrに投稿(OAuth不要・ショートカットキー: Ctrl+Shift+P)
// @author test_js
// @match *://*/*
// @exclude https://www.tumblr.com/oauth/*
// @grant GM_addStyle
// @grant window.open
// ==/UserScript==
(function () {
"use strict";
// スタイル定義
GM_addStyle(`
.tumblr-post-button {
position: absolute;
background-color: #001935;
color: white;
border: none;
border-radius: 3px;
padding: 5px 10px;
font-size: 14px;
cursor: pointer;
z-index: 9999;
display: none;
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.tumblr-post-button:hover {
background-color: #00243f;
}
.tumblr-post-button:active {
transform: translateY(1px);
}
.tumblr-image-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
z-index: 10000;
display: none;
overflow-y: auto;
}
.tumblr-image-modal-content {
background-color: white;
margin: 50px auto;
padding: 20px;
border-radius: 8px;
max-width: 800px;
max-height: 80vh;
overflow-y: auto;
position: relative;
}
.tumblr-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #ddd;
}
.tumblr-modal-title {
font-size: 18px;
font-weight: bold;
color: #001935;
margin: 0;
}
.tumblr-modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.tumblr-modal-close:hover {
color: #000;
}
.tumblr-image-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.tumblr-image-item {
border: 2px solid transparent;
border-radius: 8px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
background-color: #f8f8f8;
}
.tumblr-image-item:hover {
border-color: #001935;
transform: scale(1.05);
}
.tumblr-image-item.selected {
border-color: #001935;
box-shadow: 0 0 10px rgba(0, 25, 53, 0.3);
}
.tumblr-image-item img {
width: 100%;
height: 120px;
object-fit: cover;
display: block;
}
.tumblr-image-info {
padding: 8px;
font-size: 12px;
color: #666;
text-align: center;
}
.tumblr-modal-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 15px;
border-top: 1px solid #ddd;
}
.tumblr-selection-info {
font-size: 14px;
color: #666;
}
.tumblr-post-selected {
background-color: #001935;
color: white;
border: none;
border-radius: 5px;
padding: 10px 20px;
font-size: 14px;
cursor: pointer;
disabled: true;
}
.tumblr-post-selected:hover:not(:disabled) {
background-color: #00243f;
}
.tumblr-post-selected:disabled {
background-color: #ccc;
cursor: not-allowed;
}
`);
// ボタン要素の作成
const button = document.createElement("button");
button.textContent = "Tumblrに投稿";
button.className = "tumblr-post-button";
document.body.appendChild(button);
let selectedText = "";
let selectedImages = [];
// モーダルウィンドウの作成
const modal = document.createElement("div");
modal.className = "tumblr-image-modal";
modal.innerHTML = `
<div class="tumblr-image-modal-content">
<div class="tumblr-modal-header">
<h3 class="tumblr-modal-title">画像を選択してTumblrに投稿</h3>
<button class="tumblr-modal-close">×</button>
</div>
<div class="tumblr-image-grid" id="tumblr-image-grid">
<!-- 画像が動的に追加される -->
</div>
<div class="tumblr-modal-footer">
<div class="tumblr-selection-info">
選択済み: <span id="tumblr-selection-count">0</span>個
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button class="tumblr-post-selected" id="tumblr-post-selected" disabled>
Tumblrに投稿
</button>
<button class="tumblr-post-selected" id="tumblr-download-selected" disabled style="background-color: #00cf35;">
Tumblr(ダウンロード)
</button>
<button class="tumblr-post-selected" id="twitter-post-selected" disabled style="background-color: #1da1f2;">
Twitterに投稿
</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
// Tumblr Share Widgetでテキスト投稿
function postTextToTumblr(content, sourceUrl, sourceTitle) {
const shareUrl =
"https://www.tumblr.com/widgets/share/tool?" +
"posttype=quote&" +
"title=" +
encodeURIComponent(`出典: ${sourceTitle}`) +
"&" +
"content=" +
encodeURIComponent(`"${content}"\n\n${sourceUrl}`) +
"&" +
"canonicalUrl=" +
encodeURIComponent(sourceUrl) +
"&" +
"source=" +
encodeURIComponent(sourceUrl) +
"&" +
"shareSource=tumblr_share_button";
window.open(
shareUrl,
"_blank",
"width=700,height=600,scrollbars=yes,resizable=yes"
);
}
// Tumblr Share Widgetで画像投稿(単一画像または複数画像対応)
function postImageToTumblr(imageUrls, sourceUrl, sourceTitle) {
const captionText = `出典: ${sourceTitle}\n${sourceUrl}`;
// 単一画像の場合は配列に変換
const imageArray = Array.isArray(imageUrls) ? imageUrls : [imageUrls];
// TwitterのURL最適化(高品質画像用)
const optimizedUrls = imageArray.map((url) => {
if (url.includes("twimg.com") || url.includes("twitter.com")) {
// Twitter画像の場合は高品質版を使用
const baseUrl = url.split("?")[0];
return baseUrl + "?format=jpg&name=4096x4096";
}
return url;
});
// 複数画像の場合はカンマ区切りで結合(photoset作成)
const contentParam = optimizedUrls.join(",");
const shareUrl =
"https://www.tumblr.com/widgets/share/tool?" +
"posttype=photo&" +
"content=" +
encodeURIComponent(contentParam) +
"&" +
"caption=" +
encodeURIComponent(captionText) +
"&" +
"canonicalUrl=" +
encodeURIComponent(sourceUrl) +
"&" +
"source=" +
encodeURIComponent(sourceUrl) +
"&" +
"shareSource=tumblr_share_button";
console.log(`${optimizedUrls.length}個の画像を投稿:`, optimizedUrls);
window.open(
shareUrl,
"_blank",
"width=700,height=600,scrollbars=yes,resizable=yes"
);
}
// Tumblr Share Widgetで動画投稿
function postVideoToTumblr(videoUrl, sourceUrl, sourceTitle) {
const captionText = `出典: ${sourceTitle}\n${sourceUrl}`;
const shareUrl =
"https://www.tumblr.com/widgets/share/tool?" +
"posttype=video&" +
"embed=" +
encodeURIComponent(videoUrl) +
"&" +
"caption=" +
encodeURIComponent(captionText) +
"&" +
"canonicalUrl=" +
encodeURIComponent(sourceUrl) +
"&" +
"shareSource=tumblr_share_button";
console.log("動画を投稿:", videoUrl);
window.open(
shareUrl,
"_blank",
"width=700,height=600,scrollbars=yes,resizable=yes"
);
}
// ページ内の動画を取得
function getPageVideos() {
const videos = [];
// video要素を検索(実際に再生可能なものだけ)
const videoElements = document.querySelectorAll("video");
videoElements.forEach((video, index) => {
if ((video.src || video.currentSrc) && video.duration > 0) {
videos.push({
src: video.src || video.currentSrc,
type: "video",
title: video.title || `動画 ${index + 1}`,
element: video,
});
}
});
// iframe内の動画(YouTube、Vimeo等)を検索 - より厳密にチェック
const iframeElements = document.querySelectorAll("iframe");
iframeElements.forEach((iframe, index) => {
const src = iframe.src;
if (
src &&
iframe.offsetWidth > 200 &&
iframe.offsetHeight > 150 &&
(src.includes("youtube.com/embed") ||
src.includes("youtu.be") ||
src.includes("vimeo.com/video") ||
src.includes("dailymotion.com/embed") ||
src.includes("twitch.tv") ||
src.includes("tiktok.com"))
) {
videos.push({
src: src,
type: "iframe",
title: `埋め込み動画 ${index + 1}`,
element: iframe,
});
}
});
console.log("動画検出詳細:", {
videoElements: videoElements.length,
iframeElements: iframeElements.length,
detectedVideos: videos.length,
videos: videos,
});
return videos;
}
// ページ内の画像を取得
function getPageImages() {
const images = [];
const imgElements = document.querySelectorAll("img");
imgElements.forEach((img, index) => {
// 小さすぎる画像やアイコンを除外
if (
img.width > 50 &&
img.height > 50 &&
img.src &&
!img.src.startsWith("data:")
) {
images.push({
src: img.src,
alt: img.alt || `画像 ${index + 1}`,
width: img.width,
height: img.height,
element: img,
});
}
});
return images;
}
// Webストレージに画像を保存
async function saveImageToStorage(imageUrl, filename) {
try {
const response = await fetch(imageUrl);
const blob = await response.blob();
// FileReaderを使用してBase64に変換
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const base64Data = reader.result;
localStorage.setItem(`tumblr_image_${filename}`, base64Data);
localStorage.setItem(`tumblr_image_${filename}_type`, blob.type);
resolve(filename);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
} catch (error) {
console.error("画像の保存に失敗:", error);
throw error;
}
}
// Tumblr投稿用の画像ダウンロード機能
async function postImagesToTumblrWithDownload(
imageUrls,
sourceUrl,
sourceTitle
) {
try {
const captionText = `出典: ${sourceTitle}\n${sourceUrl}`;
// 画像をWebストレージに保存
const savedImages = [];
for (let i = 0; i < imageUrls.length; i++) {
const filename = `tumblr_download_${Date.now()}_${i}`;
console.log(`画像 ${i + 1}/${imageUrls.length} を保存中...`);
await saveImageToStorage(imageUrls[i], filename);
savedImages.push(filename);
}
// 保存された画像のリストをストレージに保存
localStorage.setItem(
"tumblr_download_saved_images",
JSON.stringify(savedImages)
);
localStorage.setItem("tumblr_download_caption", captionText);
// 画像ファイルをダウンロード(手動アップロード用 - 無効化)
// await downloadImagesAsFiles(savedImages);
// Tumblr投稿画面を開く(Share Widget API使用)
const tumblrUrl = `https://www.tumblr.com/widgets/share/tool?posttype=photo&caption=${encodeURIComponent(
captionText
)}&canonicalUrl=${encodeURIComponent(
sourceUrl
)}&source=${encodeURIComponent(
sourceUrl
)}&shareSource=tumblr_share_button`;
const tumblrWindow = window.open(
tumblrUrl,
"_blank",
"width=800,height=700,scrollbars=yes,resizable=yes"
);
// 自動アップロード機能を初期化
initTumblrAutoUpload(tumblrWindow, savedImages);
// ユーザーに手動アップロードの手順を説明(無効化)
// showTumblrUploadInstructions(savedImages.length);
// 5分後にストレージをクリア
setTimeout(() => {
clearTumblrDownloadImageStorage(savedImages);
}, 5 * 60 * 1000);
} catch (error) {
console.error("Tumblr投稿の準備に失敗:", error);
alert("Tumblr投稿の準備に失敗しました。もう一度お試しください。");
}
}
// Tumblr自動アップロード機能を初期化
function initTumblrAutoUpload(tumblrWindow, savedImages) {
if (!tumblrWindow || tumblrWindow.closed) {
console.error("Tumblr投稿画面が開けませんでした");
return;
}
console.log(`${savedImages.length}個の画像を自動アップロード開始`);
// 少し待ってからアップロード処理を開始
setTimeout(() => {
performTumblrAutoUpload(tumblrWindow, savedImages);
}, 3000);
}
// 実際のTumblr自動アップロード処理
async function performTumblrAutoUpload(tumblrWindow, savedImages) {
try {
// Tumblrウィンドウがまだ開いているかチェック
if (tumblrWindow.closed) {
console.error("Tumblr投稿画面が閉じられました");
return;
}
let tumblrDoc;
try {
tumblrDoc = tumblrWindow.document;
} catch (error) {
console.error(
"Tumblrウィンドウへのアクセス権限がありません(クロスオリジンエラー)"
);
// フォールバック: 手動アップロード手順を表示
showTumblrUploadInstructions(savedImages.length);
return;
}
// ファイルアップロード要素を検索(Tumblrの実際の構造に合わせて)
const fileInput =
tumblrDoc.querySelector('.media-upload input[type="file"]') ||
tumblrDoc.querySelector('input[name="photo"]') ||
tumblrDoc.querySelector('input[accept*="image"]') ||
tumblrDoc.querySelector('input[data-subview="upload"]') ||
tumblrDoc.querySelector('input[type="file"][multiple]');
if (!fileInput) {
console.error(
"ファイルアップロード要素が見つかりません - 再試行します"
);
// 再試行
setTimeout(
() => performTumblrAutoUpload(tumblrWindow, savedImages),
2000
);
return;
}
console.log("ファイルアップロード要素を発見:", fileInput);
// ドロップゾーンエリアも検索
const dropzoneArea =
tumblrDoc.querySelector(".dropzone-icon") ||
tumblrDoc.querySelector(".split-cell-inner") ||
tumblrDoc.querySelector("[data-js-mediauploadtext]");
// 複数の画像ファイルを準備
const files = [];
for (const filename of savedImages) {
const file = getStoredImageAsFile(filename);
if (file) {
files.push(file);
}
}
if (files.length === 0) {
console.error("有効な画像ファイルがありません");
return;
}
// FileListオブジェクトを作成
const dataTransfer = new DataTransfer();
files.forEach((file) => dataTransfer.items.add(file));
// ドロップゾーンエリアがある場合はクリックしてからファイルを設定
if (dropzoneArea) {
console.log(
"ドロップゾーンエリアを発見、クリックします:",
dropzoneArea
);
dropzoneArea.click();
// 少し待ってからファイルを設定
setTimeout(() => {
setFileInputFiles(fileInput, dataTransfer.files);
}, 500);
} else {
setFileInputFiles(fileInput, dataTransfer.files);
}
function setFileInputFiles(input, files) {
// ファイルを設定
input.files = files;
// イベントを発火
const changeEvent = new Event("change", { bubbles: true });
input.dispatchEvent(changeEvent);
const inputEvent = new Event("input", { bubbles: true });
input.dispatchEvent(inputEvent);
// さらに詳細なイベントを発火
const focusEvent = new Event("focus", { bubbles: true });
input.dispatchEvent(focusEvent);
const blurEvent = new Event("blur", { bubbles: true });
input.dispatchEvent(blurEvent);
}
console.log(`${files.length}個の画像を自動アップロードしました`);
// ストレージをクリア
setTimeout(() => {
clearTumblrDownloadImageStorage(savedImages);
}, 2000);
} catch (error) {
console.error("自動アップロード処理エラー:", error);
// エラー時は手動アップロード手順を表示
showTumblrUploadInstructions(savedImages.length);
}
}
// 画像をファイルとしてダウンロード
async function downloadImagesAsFiles(savedImages) {
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
for (let i = 0; i < savedImages.length; i++) {
const filename = savedImages[i];
const file = getStoredImageAsFile(filename);
if (file) {
// ファイルをダウンロード
const url = URL.createObjectURL(file);
const link = document.createElement("a");
link.href = url;
link.download = file.name;
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
console.log(
`画像 ${i + 1}/${savedImages.length} をダウンロードしました: ${
file.name
}`
);
// 500ms待機(ダウンロード間隔)
await delay(500);
}
}
}
// Tumblrダウンロード用のアップロード手順を表示
function showTumblrUploadInstructions(imageCount) {
const instructions = `
📸 Tumblr画像投稿の手順:
1. ${imageCount}個の画像ファイルがダウンロードされました
2. Tumblr投稿画面が開きます
3. 「写真を追加」ボタンをクリック
4. ダウンロードされた画像ファイルを選択してアップロード
5. 投稿ボタンをクリックして完了
💡 ヒント: 複数ファイルを一度に選択できます(Ctrl+クリック)
`;
alert(instructions);
}
// Twitter投稿用の画像ダウンロード機能
async function postImagesToTwitter(imageUrls, sourceUrl, sourceTitle) {
try {
const captionText = `出典: ${sourceTitle}\n${sourceUrl}`;
// 画像をWebストレージに保存
const savedImages = [];
for (let i = 0; i < imageUrls.length; i++) {
const filename = `twitter_${Date.now()}_${i}`;
console.log(`画像 ${i + 1}/${imageUrls.length} を保存中...`);
await saveImageToStorage(imageUrls[i], filename);
savedImages.push(filename);
}
// 保存された画像のリストをストレージに保存
localStorage.setItem("twitter_saved_images", JSON.stringify(savedImages));
localStorage.setItem("twitter_caption", captionText);
// 画像ファイルをダウンロード
await downloadImagesAsFiles(savedImages);
// Twitter投稿画面を開く
const twitterUrl = `https://twitter.com/compose/tweet?text=${encodeURIComponent(
captionText
)}`;
window.open(
twitterUrl,
"_blank",
"width=800,height=700,scrollbars=yes,resizable=yes"
);
// ユーザーに手動アップロードの手順を説明
showTwitterUploadInstructions(savedImages.length);
// 5分後にストレージをクリア
setTimeout(() => {
clearTwitterImageStorage(savedImages);
}, 5 * 60 * 1000);
} catch (error) {
console.error("Twitter投稿の準備に失敗:", error);
alert("Twitter投稿の準備に失敗しました。もう一度お試しください。");
}
}
// Twitter用のアップロード手順を表示
function showTwitterUploadInstructions(imageCount) {
const instructions = `
📸 Twitter画像投稿の手順:
1. ${imageCount}個の画像ファイルがダウンロードされました
2. Twitter投稿画面が開きます
3. 「メディアを追加」ボタンをクリック
4. ダウンロードされた画像ファイルを選択してアップロード
5. ツイートボタンをクリックして完了
💡 ヒント: 複数ファイルを一度に選択できます(Ctrl+クリック)
`;
alert(instructions);
}
// Tumblrダウンロード用のストレージをクリア
function clearTumblrDownloadImageStorage(savedImages) {
try {
savedImages.forEach((filename) => {
localStorage.removeItem(`tumblr_image_${filename}`);
localStorage.removeItem(`tumblr_image_${filename}_type`);
});
localStorage.removeItem("tumblr_download_saved_images");
localStorage.removeItem("tumblr_download_caption");
console.log("Tumblrダウンロードストレージをクリアしました");
} catch (error) {
console.error("Tumblrダウンロードストレージクリアエラー:", error);
}
}
// Twitter用のストレージをクリア
function clearTwitterImageStorage(savedImages) {
try {
savedImages.forEach((filename) => {
localStorage.removeItem(`tumblr_image_${filename}`);
localStorage.removeItem(`tumblr_image_${filename}_type`);
});
localStorage.removeItem("twitter_saved_images");
localStorage.removeItem("twitter_caption");
console.log("Twitterストレージをクリアしました");
} catch (error) {
console.error("Twitterストレージクリアエラー:", error);
}
}
// Webストレージから画像を取得してFileオブジェクトに変換
function getStoredImageAsFile(filename) {
const base64Data = localStorage.getItem(`tumblr_image_${filename}`);
const mimeType =
localStorage.getItem(`tumblr_image_${filename}_type`) || "image/jpeg";
if (!base64Data) {
console.error(`画像が見つかりません: ${filename}`);
return null;
}
try {
// Base64をBlobに変換
const byteCharacters = atob(base64Data.split(",")[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: mimeType });
// BlobをFileオブジェクトに変換
const fileExtension = mimeType.split("/")[1];
const file = new File([blob], `image_${filename}.${fileExtension}`, {
type: mimeType,
});
return file;
} catch (error) {
console.error("ファイル変換エラー:", error);
return null;
}
}
// 自動アップロード処理の初期化(無効化)
/*
function initAutoUpload(tumblrWindow) {
if (!tumblrWindow || tumblrWindow.closed) {
console.error('Tumblr投稿画面が開けませんでした');
return;
}
// 保存された画像を取得
const savedImagesJson = localStorage.getItem('tumblr_saved_images');
if (!savedImagesJson) {
console.error('保存された画像が見つかりません');
return;
}
const savedImages = JSON.parse(savedImagesJson);
console.log(`${savedImages.length}個の画像を自動アップロード開始`);
// 少し待ってからアップロード処理を開始
setTimeout(() => {
performAutoUpload(tumblrWindow, savedImages);
}, 2000);
}
// 実際のアップロード処理(無効化)
async function performAutoUpload(tumblrWindow, savedImages) {
try {
const tumblrDoc = tumblrWindow.document;
// ファイルアップロード要素を検索
const fileInput = tumblrDoc.querySelector('input[type="file"]') ||
tumblrDoc.querySelector('input[accept*="image"]') ||
tumblrDoc.querySelector('[data-testid="file-input"]') ||
tumblrDoc.querySelector('.file-input');
if (!fileInput) {
console.error('ファイルアップロード要素が見つかりません');
// 再試行
setTimeout(() => performAutoUpload(tumblrWindow, savedImages), 1000);
return;
}
console.log('ファイルアップロード要素を発見:', fileInput);
// 複数の画像ファイルを準備
const files = [];
for (const filename of savedImages) {
const file = getStoredImageAsFile(filename);
if (file) {
files.push(file);
}
}
if (files.length === 0) {
console.error('有効な画像ファイルがありません');
return;
}
// FileListオブジェクトを作成
const dataTransfer = new DataTransfer();
files.forEach(file => dataTransfer.items.add(file));
// ファイルを設定
fileInput.files = dataTransfer.files;
// changeイベントを発火
const changeEvent = new Event('change', { bubbles: true });
fileInput.dispatchEvent(changeEvent);
// inputイベントも発火(念のため)
const inputEvent = new Event('input', { bubbles: true });
fileInput.dispatchEvent(inputEvent);
console.log(`${files.length}個の画像を自動アップロードしました`);
// ストレージをクリア
setTimeout(() => {
savedImages.forEach(filename => {
localStorage.removeItem(`tumblr_image_${filename}`);
localStorage.removeItem(`tumblr_image_${filename}_type`);
});
localStorage.removeItem('tumblr_saved_images');
localStorage.removeItem('tumblr_caption');
console.log('ストレージをクリアしました');
}, 1000);
} catch (error) {
console.error('自動アップロード処理エラー:', error);
// 再試行
setTimeout(() => performAutoUpload(tumblrWindow, savedImages), 2000);
}
}
*/
// 画像選択モーダルを表示
function showImageModal() {
const images = getPageImages();
const videos = getPageVideos();
const grid = document.getElementById("tumblr-image-grid");
const selectionCount = document.getElementById("tumblr-selection-count");
const postButton = document.getElementById("tumblr-post-selected");
const tumblrDownloadButton = document.getElementById(
"tumblr-download-selected"
);
const twitterPostButton = document.getElementById("twitter-post-selected");
const modalTitle = document.querySelector(".tumblr-modal-title");
// Twitterサイトでは特定のボタンを非表示にする
const isTwitterSite =
window.location.hostname.includes("twitter.com") ||
window.location.hostname.includes("x.com");
if (isTwitterSite) {
if (postButton) postButton.style.display = "none";
if (twitterPostButton) twitterPostButton.style.display = "none";
} else {
if (postButton) postButton.style.display = "inline-block";
if (twitterPostButton) twitterPostButton.style.display = "inline-block";
}
// 動画が優先される場合のメッセージ
if (videos.length > 0) {
modalTitle.textContent = `動画またはメディアをTumblrに投稿 (動画: ${videos.length}個, 画像: ${images.length}個)`;
} else {
modalTitle.textContent = `画像を選択してTumblrに投稿 (${images.length}個)`;
}
// 画像が見つからない場合
if (images.length === 0) {
if (videos.length > 0) {
grid.innerHTML = `
<div style="text-align: center; padding: 40px; color: #666;">
<p>このページには${videos.length}個の動画があります。</p>
<p>動画は自動的に投稿されます。</p>
</div>
`;
} else {
grid.innerHTML =
'<div style="text-align: center; padding: 40px; color: #666;">このページには投稿可能な画像が見つかりませんでした。</div>';
}
if (postButton && postButton.style.display !== "none") {
postButton.disabled = true;
}
tumblrDownloadButton.disabled = true;
if (twitterPostButton && twitterPostButton.style.display !== "none") {
twitterPostButton.disabled = true;
}
modal.style.display = "block";
return;
}
// 選択状態をリセット
selectedImages = [];
selectionCount.textContent = "0";
if (postButton && postButton.style.display !== "none") {
postButton.disabled = true;
}
tumblrDownloadButton.disabled = true;
if (twitterPostButton && twitterPostButton.style.display !== "none") {
twitterPostButton.disabled = true;
}
// 画像グリッドを作成
grid.innerHTML = "";
images.forEach((image) => {
const item = document.createElement("div");
item.className = "tumblr-image-item";
item.innerHTML = `
<img src="${image.src}" alt="${image.alt}" loading="lazy">
<div class="tumblr-image-info">
${image.width}×${image.height}
</div>
`;
// 画像クリック時の処理
item.addEventListener("click", () => {
const isSelected = selectedImages.includes(image.src);
if (isSelected) {
// 選択解除
selectedImages = selectedImages.filter((src) => src !== image.src);
item.classList.remove("selected");
} else {
// 選択
selectedImages.push(image.src);
item.classList.add("selected");
}
// UI更新
selectionCount.textContent = selectedImages.length;
if (postButton && postButton.style.display !== "none") {
postButton.disabled = selectedImages.length === 0;
}
tumblrDownloadButton.disabled = selectedImages.length === 0;
if (twitterPostButton && twitterPostButton.style.display !== "none") {
twitterPostButton.disabled = selectedImages.length === 0;
}
});
grid.appendChild(item);
});
modal.style.display = "block";
}
// テキスト選択時の処理
document.addEventListener("mouseup", function (e) {
const selection = window.getSelection();
const text = selection.toString().trim();
if (text.length > 0) {
selectedText = text;
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
button.style.display = "block";
button.style.left = rect.left + window.scrollX + "px";
button.style.top = rect.bottom + window.scrollY + 5 + "px";
} else {
if (!button.contains(e.target)) {
button.style.display = "none";
}
}
});
// ボタンクリック時の処理
button.addEventListener("click", function (e) {
e.stopPropagation();
if (selectedText) {
postTextToTumblr(selectedText, window.location.href, document.title);
button.style.display = "none";
window.getSelection().removeAllRanges();
}
});
// モーダルのイベントリスナー
const closeButton = modal.querySelector(".tumblr-modal-close");
const postSelectedButton = document.getElementById("tumblr-post-selected");
const tumblrDownloadButton = document.getElementById(
"tumblr-download-selected"
);
const twitterPostButton = document.getElementById("twitter-post-selected");
// モーダルを閉じる
closeButton.addEventListener("click", () => {
modal.style.display = "none";
});
// モーダル背景クリックで閉じる
modal.addEventListener("click", (e) => {
if (e.target === modal) {
modal.style.display = "none";
}
});
// 選択した画像を投稿(URL方式でTumblrへ)
postSelectedButton.addEventListener("click", async () => {
if (selectedImages.length > 0) {
postSelectedButton.disabled = true;
postSelectedButton.textContent = "処理中...";
try {
// Tumblrに画像URLで投稿
postImageToTumblr(selectedImages, window.location.href, document.title);
modal.style.display = "none";
} catch (error) {
console.error("投稿処理エラー:", error);
} finally {
postSelectedButton.disabled = false;
postSelectedButton.textContent = "Tumblrに投稿";
}
}
});
// Tumblrダウンロード投稿ボタンのイベントリスナー
tumblrDownloadButton.addEventListener("click", async () => {
if (selectedImages.length > 0) {
tumblrDownloadButton.disabled = true;
tumblrDownloadButton.textContent = "処理中...";
try {
// Tumblrに画像ダウンロード方式で投稿
await postImagesToTumblrWithDownload(
selectedImages,
window.location.href,
document.title
);
modal.style.display = "none";
} catch (error) {
console.error("Tumblrダウンロード投稿処理エラー:", error);
} finally {
tumblrDownloadButton.disabled = false;
tumblrDownloadButton.textContent = "Tumblr(ダウンロード)";
}
}
});
// Twitter投稿ボタンのイベントリスナー
twitterPostButton.addEventListener("click", async () => {
if (selectedImages.length > 0) {
twitterPostButton.disabled = true;
twitterPostButton.textContent = "処理中...";
try {
// Twitterに画像ダウンロード方式で投稿
await postImagesToTwitter(
selectedImages,
window.location.href,
document.title
);
modal.style.display = "none";
} catch (error) {
console.error("Twitter投稿処理エラー:", error);
} finally {
twitterPostButton.disabled = false;
twitterPostButton.textContent = "Twitterに投稿";
}
}
});
// Tumblrサイトでの自動アップロード処理
function initTumblrSiteAutoUpload() {
if (!window.location.hostname.includes("tumblr.com")) {
return;
}
// 保存された画像があるかチェック
const savedImagesJson = localStorage.getItem(
"tumblr_download_saved_images"
);
if (!savedImagesJson) {
return;
}
const savedImages = JSON.parse(savedImagesJson);
console.log(
`Tumblrサイトで${savedImages.length}個の画像を自動アップロード開始`
);
// 少し待ってからアップロード処理を開始
setTimeout(() => {
performTumblrSiteAutoUpload(savedImages);
}, 2000);
}
// Tumblrサイトでの実際の自動アップロード処理
async function performTumblrSiteAutoUpload(savedImages) {
try {
// ファイルアップロード要素を検索(Tumblrの実際の構造に合わせて)
const fileInput =
document.querySelector('.media-upload input[type="file"]') ||
document.querySelector('input[name="photo"]') ||
document.querySelector('input[accept*="image"]') ||
document.querySelector('input[data-subview="upload"]') ||
document.querySelector('input[type="file"][multiple]');
if (!fileInput) {
console.error(
"ファイルアップロード要素が見つかりません - 再試行します"
);
// 再試行
setTimeout(() => performTumblrSiteAutoUpload(savedImages), 2000);
return;
}
console.log("ファイルアップロード要素を発見:", fileInput);
// ドロップゾーンエリアも検索
const dropzoneArea =
document.querySelector(".dropzone-icon") ||
document.querySelector(".split-cell-inner") ||
document.querySelector("[data-js-mediauploadtext]");
// 複数の画像ファイルを準備
const files = [];
for (const filename of savedImages) {
const file = getStoredImageAsFile(filename);
if (file) {
files.push(file);
}
}
if (files.length === 0) {
console.error("有効な画像ファイルがありません");
return;
}
// FileListオブジェクトを作成
const dataTransfer = new DataTransfer();
files.forEach((file) => dataTransfer.items.add(file));
// ドロップゾーンエリアがある場合はクリックしてからファイルを設定
if (dropzoneArea) {
console.log(
"ドロップゾーンエリアを発見、クリックします:",
dropzoneArea
);
dropzoneArea.click();
// 少し待ってからファイルを設定
setTimeout(() => {
setFileInputFiles(fileInput, dataTransfer.files);
}, 500);
} else {
setFileInputFiles(fileInput, dataTransfer.files);
}
function setFileInputFiles(input, files) {
// ファイルを設定
input.files = files;
// イベントを発火
const changeEvent = new Event("change", { bubbles: true });
input.dispatchEvent(changeEvent);
const inputEvent = new Event("input", { bubbles: true });
input.dispatchEvent(inputEvent);
// さらに詳細なイベントを発火
const focusEvent = new Event("focus", { bubbles: true });
input.dispatchEvent(focusEvent);
const blurEvent = new Event("blur", { bubbles: true });
input.dispatchEvent(blurEvent);
}
console.log(`${files.length}個の画像を自動アップロードしました`);
// ストレージをクリア
setTimeout(() => {
clearTumblrDownloadImageStorage(savedImages);
}, 1000);
} catch (error) {
console.error("Tumblrサイト自動アップロード処理エラー:", error);
}
}
// ショートカットキー (Ctrl+Shift+P) の処理
document.addEventListener("keydown", function (e) {
if (e.ctrlKey && e.shiftKey && e.key === "P") {
e.preventDefault();
const selection = window.getSelection();
const text = selection.toString().trim();
if (text.length > 0) {
postTextToTumblr(text, window.location.href, document.title);
window.getSelection().removeAllRanges();
} else {
// テキストが選択されていない場合
const videos = getPageVideos();
console.log("動画検出結果:", videos);
// 動画優先機能を一時的に無効化し、常に画像選択モーダルを表示
console.log("画像選択モーダルを表示");
showImageModal();
// 動画がある場合の処理(現在は無効化)
/*
if (videos.length > 0) {
// 動画が存在する場合は動画投稿を優先
console.log('動画投稿を実行:', videos[0].src);
postVideoToTumblr(videos[0].src, window.location.href, document.title);
} else {
// 動画がない場合は画像選択モーダルを表示
console.log('画像選択モーダルを表示');
showImageModal();
}
*/
}
}
});
// スクロール時にボタンを非表示
window.addEventListener("scroll", function () {
button.style.display = "none";
});
// クリック時に選択以外の場所ならボタンを非表示
document.addEventListener("click", function (e) {
if (!button.contains(e.target)) {
button.style.display = "none";
}
});
// 初期化処理
document.addEventListener("DOMContentLoaded", function () {
// Tumblrサイトでの自動アップロード機能を初期化
initTumblrSiteAutoUpload();
});
// DOMContentLoadedが既に発火している場合の対策
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initTumblrSiteAutoUpload);
} else {
initTumblrSiteAutoUpload();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment