// ==UserScript==
// @name nicovideo downloader
// @description script for downloading videos from the nicovideo website
// @namespace kwlNjR37xBCMkr76P5eKA88apmOClCfZ
// @author kwlNjR37xBCMkr76P5eKA88apmOClCfZ
// @version 0001
// @match *://www.nicovideo.jp/watch/sm*
// @match *://ext.nicovideo.jp/?sm*
// @website https://greasyfork.org/en/scripts/548403
// @icon https://www.google.com/s2/favicons?domain=ext.nicovideo.jp
// @grant GM.setValue
// @grant GM.getValue
// @license CC0 1.0
// ==/UserScript==
(function() {
'use strict';
let targetContainer;
let observer;
const SCRIPT_SRC_720p = "https://www.nicozon.net/js/bookmarklet_old_720p.js";
const SCRIPT_SRC_480p = "https://www.nicozon.net/js/bookmarklet_old_480p.js";
const SCRIPT_SRC_360p = "https://www.nicozon.net/js/bookmarklet_old_360p.js";
const SCRIPT_SRC_audio = "https://www.nicozon.net/js/bookmarklet_old_audio.js";
// Log script initialization
console.log('NicoVideo Downloader script initialized');
// Function to extract video ID from URL
function getVideoId() {
try {
const url = window.location.href;
const match = url.match(/sm(\d+)/);
const videoId = match ? match[0] : null;
if (!videoId) {
console.error('Could not extract video ID from URL:', url);
} else {
console.log('Extracted video ID:', videoId);
}
return videoId;
} catch (error) {
console.error('Error extracting video ID:', error);
return null;
}
}
// Function to create download links
function createDownloadLinks() {
try {
const videoId = getVideoId();
if (!videoId) {
console.error('Cannot create download links without video ID');
return null;
}
// Create container for links
const linksContainer = document.createElement('div');
linksContainer.className = 'download-links';
linksContainer.style.cssText = `
display: inline-block;
margin-left: 10px;
font-size: 14px;
`;
// Add label
const label = document.createElement('span');
label.textContent = 'Download: ';
label.style.marginRight = '8px';
linksContainer.appendChild(label);
// Create download options
const options = [
{ text: '720p', quality: '720p', script: SCRIPT_SRC_720p },
{ text: '480p', quality: '480p', script: SCRIPT_SRC_480p },
{ text: '360p', quality: '360p', script: SCRIPT_SRC_360p },
{ text: 'Audio', quality: 'audio', script: SCRIPT_SRC_audio }
];
options.forEach((option, index) => {
const link = document.createElement('a');
link.href = `https://ext.nicovideo.jp/?${videoId}`;
link.textContent = option.text;
link.dataset.quality = option.quality;
link.dataset.script = option.script;
link.style.cssText = `
color: #ff9900;
text-decoration: none;
margin: 0 4px;
font-weight: bold;
cursor: pointer;
`;
link.addEventListener('mouseover', function() {
this.style.textDecoration = 'underline';
});
link.addEventListener('mouseout', function() {
this.style.textDecoration = 'none';
});
link.addEventListener('click', async function(e) {
e.preventDefault();
try {
// Store the selected quality using GM.setValue
await GM.setValue('nicovideoDownloadQuality', this.dataset.quality);
await GM.setValue('nicovideoDownloadScript', this.dataset.script);
console.log('Stored download preference:', this.dataset.quality);
window.open(this.href, '_blank');
} catch (error) {
console.error('Error storing download preference:', error);
alert('Error saving download preference. Please check console for details.');
}
});
linksContainer.appendChild(link);
// Add separator except for the last item
if (index < options.length - 1) {
const separator = document.createElement('span');
separator.textContent = '|';
separator.style.cssText = 'color: #ccc; margin: 0 4px;';
linksContainer.appendChild(separator);
}
});
console.log('Download links created successfully');
return linksContainer;
} catch (error) {
console.error('Error creating download links:', error);
return null;
}
}
function addDownloadLinks() {
try {
targetContainer = document.querySelector('.d_flex.gap_x2.text-layer_lowEm.fs_s.fw_bold.white-space_nowrap.ai_center');
if (targetContainer) {
// Check if links already exist to avoid duplicates
if (!targetContainer.querySelector('.download-links')) {
const linksContainer = createDownloadLinks();
if (linksContainer) {
targetContainer.appendChild(linksContainer);
console.log('Download links added to page');
// Stop observing once we've successfully added the links
if (observer) {
observer.disconnect();
console.log('MutationObserver disconnected');
}
}
} else {
console.log('Download links already exist, skipping');
}
} else {
console.log('Target container not found yet');
return false;
}
return true;
} catch (error) {
console.error('Error adding download links:', error);
return false;
}
}
// Function to handle different page types
async function handlePage() {
try {
console.log('Handling page:', window.location.hostname);
const videoId = getVideoId();
if (!videoId) {
console.error('No video ID found, stopping execution');
return;
}
if (window.location.hostname === 'www.nicovideo.jp') {
console.log('On main nicovideo page');
// Try immediately first
const success = addDownloadLinks();
// If not found, set up MutationObserver to watch for DOM changes
if (!success) {
console.log('Setting up MutationObserver to watch for target container');
observer = new MutationObserver(function(mutations) {
console.log('DOM mutation observed', mutations.length, 'changes');
mutations.forEach(function(mutation) {
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
addDownloadLinks();
}
});
});
// Start observing the document body for added nodes
observer.observe(document.body, {
childList: true,
subtree: true
});
// Set a timeout as fallback
/*setTimeout(() => {
if (observer) {
observer.disconnect();
console.log('MutationObserver disconnected after timeout');
}
const finalAttempt = addDownloadLinks();
if (!finalAttempt) {
console.error('Failed to add download links after timeout');
}
}, 30000); // 30 seconds as fallback*/
}
}
else if (window.location.hostname === 'ext.nicovideo.jp') {
console.log('On ext.nicovideo page');
// On ext.nicovideo page, check if we have a stored quality preference
const quality = await GM.getValue('nicovideoDownloadQuality');
const scriptSrc = await GM.getValue('nicovideoDownloadScript');
console.log('Retrieved stored values - Quality:', quality, '; Script:', scriptSrc);
const p = document.createElement("p");
p.textContent = "Download: " + (quality || 'No quality selected');
document.body.appendChild(p);
// Clean up stored values
await GM.setValue('nicovideoDownloadQuality', '');
await GM.setValue('nicovideoDownloadScript', '');
console.log('Cleared stored values');
if (scriptSrc) {
console.log('Loading download script:', scriptSrc);
try {
const script = document.createElement('script');
script.setAttribute('charset', 'utf-8');
script.src = scriptSrc;
script.onload = function() {
console.log('Download script loaded successfully');
};
script.onerror = function() {
console.error('Failed to load download script:', scriptSrc);
alert('Failed to load download script. Please try again.');
};
document.body.appendChild(script);
} catch (error) {
console.error('Error loading download script:', error);
}
} else {
console.log('No script source found, creating download links instead');
const linksContainer = createDownloadLinks();
if (linksContainer) {
document.body.appendChild(linksContainer);
}
}
} else {
console.warn('Unsupported hostname:', window.location.hostname);
}
} catch (error) {
console.error('Error in handlePage function:', error);
}
}
// Run when DOM is ready
if (document.readyState === 'loading') {
console.log('DOM not ready, adding DOMContentLoaded listener');
document.addEventListener('DOMContentLoaded', function() {
console.log('DOMContentLoaded fired');
handlePage();
});
} else {
console.log('DOM already ready, executing immediately');
handlePage();
}
})();
// https://www.youtube.com/watch?v=sztJ8cO4mak
// https://www.nicozon.net/