From cc19bbd372ae0df779aabb68b78381cf7d96837e Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 2 Feb 2026 20:17:13 -0500 Subject: [PATCH] added functionality for pixiv and deviantart --- backend/app/api/credentials.py | 2 +- backend/app/api/platforms.py | 42 ++++++++++- backend/app/api/sources.py | 2 +- backend/app/api/subscriptions.py | 2 +- backend/app/services/gallery_dl.py | 21 ++++++ extension/manifest.json | 11 ++- extension/popup/popup.js | 65 +++++++++++++----- .../gallerysubscriber-1.1.0.zip | Bin 0 -> 19591 bytes extension/web-ext-config.cjs | 10 +++ frontend/src/views/Credentials.vue | 4 ++ frontend/src/views/Dashboard.vue | 2 + frontend/src/views/Settings.vue | 4 ++ frontend/src/views/SourceForm.vue | 2 + frontend/src/views/Sources.vue | 6 ++ frontend/src/views/Subscriptions.vue | 6 ++ summary.md | 6 +- 16 files changed, 158 insertions(+), 27 deletions(-) create mode 100644 extension/web-ext-artifacts/gallerysubscriber-1.1.0.zip create mode 100644 extension/web-ext-config.cjs diff --git a/backend/app/api/credentials.py b/backend/app/api/credentials.py index a88567a..75136dd 100644 --- a/backend/app/api/credentials.py +++ b/backend/app/api/credentials.py @@ -12,7 +12,7 @@ from app.utils.encryption import encrypt_data, decrypt_data bp = Blueprint("credentials", __name__) -VALID_PLATFORMS = ["patreon", "subscribestar", "hentaifoundry", "discord"] +VALID_PLATFORMS = ["patreon", "subscribestar", "hentaifoundry", "discord", "pixiv", "deviantart"] @bp.route("", methods=["GET"]) diff --git a/backend/app/api/platforms.py b/backend/app/api/platforms.py index 54de77b..9c6d384 100644 --- a/backend/app/api/platforms.py +++ b/backend/app/api/platforms.py @@ -85,6 +85,44 @@ async def list_platforms(): "default_config": gdl_service.get_default_config_for_platform("discord"), "notes": "Requires Discord user token (not bot token)", }, + "pixiv": { + "name": "Pixiv", + "description": "Download artwork from Pixiv artists", + "requires_auth": True, + "auth_type": "cookies", + "url_pattern": "https://www.pixiv.net/users/{user_id}", + "url_examples": [ + "https://www.pixiv.net/users/12345678", + "https://www.pixiv.net/en/users/12345678", + ], + "content_types": gdl_service.get_platform_content_types("pixiv"), + "content_type_descriptions": { + "all": "All artwork (illustrations and manga)", + "illusts": "Illustrations only", + "manga": "Manga only", + "bookmarks": "Bookmarked works", + }, + "default_config": gdl_service.get_default_config_for_platform("pixiv"), + }, + "deviantart": { + "name": "DeviantArt", + "description": "Download artwork from DeviantArt artists", + "requires_auth": False, + "auth_type": "cookies", + "url_pattern": "https://www.deviantart.com/{username}", + "url_examples": [ + "https://www.deviantart.com/example-artist", + "https://www.deviantart.com/example-artist/gallery", + ], + "content_types": gdl_service.get_platform_content_types("deviantart"), + "content_type_descriptions": { + "all": "All deviations", + "gallery": "Gallery artwork", + "scraps": "Scraps folder", + "favorites": "Favorited works", + }, + "default_config": gdl_service.get_default_config_for_platform("deviantart"), + }, } return jsonify({"platforms": platforms}) @@ -95,7 +133,7 @@ async def get_platform(platform: str): """Get detailed information about a specific platform.""" gdl_service = GalleryDLService() - if platform not in ["patreon", "subscribestar", "hentaifoundry", "discord"]: + if platform not in ["patreon", "subscribestar", "hentaifoundry", "discord", "pixiv", "deviantart"]: return jsonify({"error": f"Unknown platform: {platform}"}), 404 # Get the full platform info from list_platforms @@ -115,7 +153,7 @@ async def get_config_schema(platform: str): This defines what options can be set per-source for this platform. Useful for building dynamic forms in the frontend. """ - if platform not in ["patreon", "subscribestar", "hentaifoundry", "discord"]: + if platform not in ["patreon", "subscribestar", "hentaifoundry", "discord", "pixiv", "deviantart"]: return jsonify({"error": f"Unknown platform: {platform}"}), 404 gdl_service = GalleryDLService() diff --git a/backend/app/api/sources.py b/backend/app/api/sources.py index ca70acd..3f6628b 100644 --- a/backend/app/api/sources.py +++ b/backend/app/api/sources.py @@ -78,7 +78,7 @@ async def create_source(): return jsonify({"error": f"Missing required fields: {', '.join(missing)}"}), 400 # Validate platform - valid_platforms = ["patreon", "subscribestar", "hentaifoundry", "discord"] + valid_platforms = ["patreon", "subscribestar", "hentaifoundry", "discord", "pixiv", "deviantart"] if data["platform"] not in valid_platforms: return jsonify({"error": f"Invalid platform. Must be one of: {', '.join(valid_platforms)}"}), 400 diff --git a/backend/app/api/subscriptions.py b/backend/app/api/subscriptions.py index af38375..9fe6580 100644 --- a/backend/app/api/subscriptions.py +++ b/backend/app/api/subscriptions.py @@ -232,7 +232,7 @@ async def add_source_to_subscription(subscription_id: int): return jsonify({"error": f"Missing required fields: {', '.join(missing)}"}), 400 # Validate platform - valid_platforms = ["patreon", "subscribestar", "hentaifoundry", "discord"] + valid_platforms = ["patreon", "subscribestar", "hentaifoundry", "discord", "pixiv", "deviantart"] if data["platform"] not in valid_platforms: return jsonify({"error": f"Invalid platform. Must be one of: {', '.join(valid_platforms)}"}), 400 diff --git a/backend/app/services/gallery_dl.py b/backend/app/services/gallery_dl.py index e84ae99..5c1ef66 100644 --- a/backend/app/services/gallery_dl.py +++ b/backend/app/services/gallery_dl.py @@ -171,6 +171,23 @@ class GalleryDLService: # Deep crawling options "threads": True, # Include thread content for complete history }, + "pixiv": { + "content_types": ["all"], + "directory": ["{category}"], + "filename": "{id}_{title[:50]}_{num:>02}.{extension}", + # Content options + "ugoira": True, # Download ugoira (animated) as WebM + }, + "deviantart": { + "content_types": ["all"], + "directory": [], # Flat structure within platform folder + "filename": "{index:>03}_{title[:50]}.{extension}", + # Content options + "flat": True, # Flatten folder structure + "original": True, # Download original quality + "mature": True, # Include mature content + "metadata": True, # Include metadata + }, } def __init__(self, rate_limit: Optional[float] = None): @@ -682,6 +699,10 @@ class GalleryDLService: return ["all"] # No granular control elif platform == "discord": return ["all"] # No granular control + elif platform == "pixiv": + return ["all", "illusts", "manga", "bookmarks"] + elif platform == "deviantart": + return ["all", "gallery", "scraps", "favorites"] else: return ["all"] diff --git a/extension/manifest.json b/extension/manifest.json index 63b2961..80e9af4 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,12 +1,15 @@ { "manifest_version": 2, "name": "GallerySubscriber", - "version": "1.0.0", + "version": "1.1.0", "description": "Export cookies from supported platforms to GallerySubscriber for automated downloads", "browser_specific_settings": { "gecko": { - "id": "gallerysubscriber@fabledsword.com" + "id": "gallerysubscriber@fabledsword.com", + "data_collection_permissions": { + "required": ["none"] + } } }, @@ -20,7 +23,9 @@ "*://*.subscribestar.com/*", "*://*.subscribestar.adult/*", "*://*.hentai-foundry.com/*", - "*://*.discord.com/*" + "*://*.discord.com/*", + "*://*.pixiv.net/*", + "*://*.deviantart.com/*" ], "browser_action": { diff --git a/extension/popup/popup.js b/extension/popup/popup.js index e7b0114..c44d300 100644 --- a/extension/popup/popup.js +++ b/extension/popup/popup.js @@ -11,7 +11,13 @@ document.addEventListener('DOMContentLoaded', async () => { document.getElementById('setup-required').classList.remove('hidden'); const alertDiv = document.querySelector('#setup-required .alert'); if (alertDiv) { - alertDiv.innerHTML = `Error

${error.message}

`; + alertDiv.textContent = ''; + const strong = document.createElement('strong'); + strong.textContent = 'Error'; + const p = document.createElement('p'); + p.textContent = error.message; + alertDiv.appendChild(strong); + alertDiv.appendChild(p); alertDiv.className = 'alert alert-error'; } } @@ -51,7 +57,11 @@ async function init() { // Show loading state for platforms const platformsContainer = document.getElementById('platforms-list'); - platformsContainer.innerHTML = '
Loading platforms...
'; + platformsContainer.textContent = ''; + const loadingDiv = document.createElement('div'); + loadingDiv.style.cssText = 'text-align: center; padding: 20px; color: var(--text-secondary);'; + loadingDiv.textContent = 'Loading platforms...'; + platformsContainer.appendChild(loadingDiv); // Now do background tasks in parallel (non-blocking) // Check if we should test connection (rate limited) @@ -134,7 +144,7 @@ function updateConnectionIndicator(connected) { async function loadPlatformStatus() { const status = await browser.runtime.sendMessage({ type: 'GET_PLATFORM_STATUS' }); const container = document.getElementById('platforms-list'); - container.innerHTML = ''; + container.textContent = ''; for (const [key, platform] of Object.entries(PLATFORMS)) { const platformStatus = status[key] || {}; @@ -167,20 +177,41 @@ function createPlatformCard(key, platform, status) { const statusText = getStatusText(status, platform, key); const statusClass = getStatusClass(status, platform, key); - card.innerHTML = ` -
- ${platform.name.charAt(0)} -
-
-
${platform.name}
-
${statusText}
-
- - - - - - `; + // Build card content using DOM methods (avoids innerHTML security warnings) + const iconDiv = document.createElement('div'); + iconDiv.className = 'platform-icon'; + iconDiv.style.backgroundColor = platform.color; + iconDiv.textContent = platform.name.charAt(0); + + const infoDiv = document.createElement('div'); + infoDiv.className = 'info'; + + const nameDiv = document.createElement('div'); + nameDiv.className = 'name'; + nameDiv.textContent = platform.name; + + const statusDiv = document.createElement('div'); + statusDiv.className = `status ${statusClass}`; + statusDiv.textContent = statusText; + + infoDiv.appendChild(nameDiv); + infoDiv.appendChild(statusDiv); + + const actionSpan = document.createElement('span'); + actionSpan.className = 'action-icon'; + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('width', '20'); + svg.setAttribute('height', '20'); + svg.setAttribute('viewBox', '0 0 24 24'); + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('fill', 'currentColor'); + path.setAttribute('d', 'M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z'); + svg.appendChild(path); + actionSpan.appendChild(svg); + + card.appendChild(iconDiv); + card.appendChild(infoDiv); + card.appendChild(actionSpan); if (!isDisabled && !isDiscordWithoutToken) { card.addEventListener('click', () => exportPlatformCookies(key, card)); diff --git a/extension/web-ext-artifacts/gallerysubscriber-1.1.0.zip b/extension/web-ext-artifacts/gallerysubscriber-1.1.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..0bf0ddf5bd49459eb449cc158b9fc1344bda995f GIT binary patch literal 19591 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 Xi^`Vz`sJavNNte;AfDN4D|+>RG4N(C literal 0 HcmV?d00001 diff --git a/extension/web-ext-config.cjs b/extension/web-ext-config.cjs new file mode 100644 index 0000000..729ffce --- /dev/null +++ b/extension/web-ext-config.cjs @@ -0,0 +1,10 @@ +module.exports = { + ignoreFiles: [ + "create-icons.py", + "generate-icons.py", + "web-ext-artifacts", + "web-ext-config.js", + ".amo-upload-uuid", + "*.md" + ] +}; diff --git a/frontend/src/views/Credentials.vue b/frontend/src/views/Credentials.vue index dee71f9..3b58ae5 100644 --- a/frontend/src/views/Credentials.vue +++ b/frontend/src/views/Credentials.vue @@ -235,6 +235,8 @@ function getPlatformIcon(platform) { subscribestar: 'mdi-star', hentaifoundry: 'mdi-palette', discord: 'mdi-discord', + pixiv: 'mdi-alpha-p-box', + deviantart: 'mdi-deviantart', } return icons[platform] || 'mdi-web' } @@ -245,6 +247,8 @@ function getPlatformColor(platform) { subscribestar: '#FFD700', hentaifoundry: '#9C27B0', discord: '#5865F2', + pixiv: '#0096FA', + deviantart: '#05CC47', } return colors[platform] || 'grey' } diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue index 536b3cf..eaa2913 100644 --- a/frontend/src/views/Dashboard.vue +++ b/frontend/src/views/Dashboard.vue @@ -520,6 +520,8 @@ function getPlatformIcon(platform) { subscribestar: 'mdi-star', hentaifoundry: 'mdi-palette', discord: 'mdi-discord', + pixiv: 'mdi-alpha-p-box', + deviantart: 'mdi-deviantart', } return icons[platform] || 'mdi-web' } diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue index 330ca34..beb64ad 100644 --- a/frontend/src/views/Settings.vue +++ b/frontend/src/views/Settings.vue @@ -539,6 +539,8 @@ function getPlatformIcon(platform) { subscribestar: 'mdi-star', hentaifoundry: 'mdi-palette', discord: 'mdi-discord', + pixiv: 'mdi-alpha-p-box', + deviantart: 'mdi-deviantart', } return icons[platform] || 'mdi-web' } @@ -549,6 +551,8 @@ function getPlatformColor(platform) { subscribestar: '#FFD700', hentaifoundry: '#9C27B0', discord: '#5865F2', + pixiv: '#0096FA', + deviantart: '#05CC47', } return colors[platform] || 'grey' } diff --git a/frontend/src/views/SourceForm.vue b/frontend/src/views/SourceForm.vue index 90f323f..7c9c83a 100644 --- a/frontend/src/views/SourceForm.vue +++ b/frontend/src/views/SourceForm.vue @@ -199,6 +199,8 @@ const platformOptions = [ { title: 'SubscribeStar', value: 'subscribestar' }, { title: 'Hentai Foundry', value: 'hentaifoundry' }, { title: 'Discord', value: 'discord' }, + { title: 'Pixiv', value: 'pixiv' }, + { title: 'DeviantArt', value: 'deviantart' }, ] const urlHint = computed(() => { diff --git a/frontend/src/views/Sources.vue b/frontend/src/views/Sources.vue index 54b8769..0d887d6 100644 --- a/frontend/src/views/Sources.vue +++ b/frontend/src/views/Sources.vue @@ -150,6 +150,8 @@ const platformOptions = [ { title: 'SubscribeStar', value: 'subscribestar' }, { title: 'Hentai Foundry', value: 'hentaifoundry' }, { title: 'Discord', value: 'discord' }, + { title: 'Pixiv', value: 'pixiv' }, + { title: 'DeviantArt', value: 'deviantart' }, ] const enabledOptions = [ @@ -189,6 +191,8 @@ function getPlatformIcon(platform) { subscribestar: 'mdi-star', hentaifoundry: 'mdi-palette', discord: 'mdi-discord', + pixiv: 'mdi-alpha-p-box', + deviantart: 'mdi-deviantart', } return icons[platform] || 'mdi-web' } @@ -199,6 +203,8 @@ function getPlatformColor(platform) { subscribestar: 'amber', hentaifoundry: 'purple', discord: 'indigo', + pixiv: 'blue', + deviantart: 'green', } return colors[platform] || 'grey' } diff --git a/frontend/src/views/Subscriptions.vue b/frontend/src/views/Subscriptions.vue index bdf456b..f58e3bb 100644 --- a/frontend/src/views/Subscriptions.vue +++ b/frontend/src/views/Subscriptions.vue @@ -381,6 +381,8 @@ const platformOptions = [ { title: 'SubscribeStar', value: 'subscribestar' }, { title: 'Hentai Foundry', value: 'hentaifoundry' }, { title: 'Discord', value: 'discord' }, + { title: 'Pixiv', value: 'pixiv' }, + { title: 'DeviantArt', value: 'deviantart' }, ] const headers = [ @@ -423,6 +425,8 @@ function getPlatformIcon(platform) { subscribestar: 'mdi-star', hentaifoundry: 'mdi-palette', discord: 'mdi-discord', + pixiv: 'mdi-alpha-p-box', + deviantart: 'mdi-deviantart', } return icons[platform] || 'mdi-web' } @@ -433,6 +437,8 @@ function getPlatformColor(platform) { subscribestar: 'amber', hentaifoundry: 'purple', discord: 'indigo', + pixiv: 'blue', + deviantart: 'green', } return colors[platform] || 'grey' } diff --git a/summary.md b/summary.md index 4550c7a..4893105 100644 --- a/summary.md +++ b/summary.md @@ -1,10 +1,10 @@ # GallerySubscriber - Project Summary -**Updated:** 2026-01-30 (Error recognition improvements, analysis tooling) +**Updated:** 2026-02-02 (Added Pixiv and DeviantArt platform support) ## Overview -GallerySubscriber is a Docker-based web application that automates downloading content from subscription platforms (Patreon, SubscribeStar, Discord, Hentai Foundry). It combines a Vue 3 web dashboard, Firefox extension for credential export, and a Python backend with Celery for background task scheduling. +GallerySubscriber is a Docker-based web application that automates downloading content from subscription platforms (Patreon, SubscribeStar, Discord, Hentai Foundry, Pixiv, DeviantArt). It combines a Vue 3 web dashboard, Firefox extension for credential export, and a Python backend with Celery for background task scheduling. **Core Purpose:** Automatically download and organize content from multiple subscription platforms using gallery-dl as the underlying download engine. @@ -159,6 +159,8 @@ Download (1) ──→ (many) ContentItem | SubscribeStar Adult | Cookies | images, attachments | Infinite scroll (complete) | | Hentai Foundry | Cookies | pictures, scraps, stories | All pages (complete) | | Discord | Token | messages, attachments, embeds | All threads included | +| Pixiv | Cookies | illusts, manga, bookmarks | All works (complete) | +| DeviantArt | Cookies (optional) | gallery, scraps, favorites | All deviations (complete) | **Note:** All platforms fetch complete history by default - no depth limits. Gallery-dl paginates through ALL available content.