您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
画像 & YouTube動画読込表示 / 画像レス & 板リンク抽出 / jump(リンク中継ページ)無効 / 規制回避リンク認識 / シンプルな見た目
// ==UserScript== // @name 旧5ch 画像&動画etc // @namespace http://tampermonkey.net/ // @version 1.2.4 // @description 画像 & YouTube動画読込表示 / 画像レス & 板リンク抽出 / jump(リンク中継ページ)無効 / 規制回避リンク認識 / シンプルな見た目 // @author 匿名Cat // @match https://*.5ch.net/test/read.cgi/*/* // @match http://*.5ch.net/test/read.cgi/*/* // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js // @resource bootstrap.min.css https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css // @icon https://www.google.com/s2/favicons?domain=5ch.net // @grant GM_download // @grant GM_addStyle // @grant GM_getResourceText // @license MIT // ==/UserScript== (function() { 'use strict'; console.log('5ch 画像&動画') $?.noConflict() // 設定開始================ 'jpg png webp jpeg gif' const settings = { imgExts: 'jpg png webp jpeg gif'.split(' '), keys: { download: ['d'], removePreview: ['c', 'Escape']} } // 直接生成型画像 サイズ const size = '8rem' const userScriptId = "ch_im_and_video__" // 設定終了============== // bootstrap style読み込み GM_addStyle(GM_getResourceText("bootstrap.min.css")) // util const d = document const isImageUrl = url => new RegExp(`\\.${settings.imgExts.join('|')}$`, 'g').test(url) const utilUnion = arr => [...new Set(arr)] const optionalHttps = hrefStr => /^https?:\/\//.test(hrefStr) ? hrefStr : 'https://' + hrefStr const remove5chJump = url => { const mtc = url.match(/(?:https?:\/\/jump\.5ch\.net\/\?)(.+)/) return mtc?.[1] ?? url } const style = `html { font-size: 1rem; } /* 画像プレビュー周り */ .container.container_body { width: 100%; max-width: 100%; margin: 0; padding: 0; display: flex; position: relative; } .container.container_body>.contents { width: 60vw; padding-left: 4vw; height: 100vh; overflow: auto; resize: horizontal; } .side-container { flex: 1; position: relative; } .settings-win { position: absolute; top: 0; } .preview-img-box:before { content: ""; display: block; padding-top: 75%; } .preview-img-box>img { position: absolute; top: 0; left: 0; bottom: 0; right: 0; width: 100%; max-height: 100vh; object-fit: contain; z-index: 3;} a.image { font-size: 0; } /* ナビゲーションバー */ #${userScriptId} { position: sticky; top: 0; background-color: #fff; } #${userScriptId} .title { margin-top: 0; padding-top: 20px; } #${userScriptId}extract_im { cursor: pointer; } #${userScriptId} .related-thread { display: inline-block; } #${userScriptId}relations { font-size: inherit; } /* 5ch公式のGUI */ .topmenu.centered,.bottommenu.centered,.input-group { display: none; } .submitbtn.btn { font-size: inherit; } .rBtn { border: none; } .post_hover { z-index: 3; } .menujust { margin:0; padding-left: 0; } /* お絵かき */ .wPaint-canvas { box-shadow: 0 0 2rem rgba(0, 0, 0, .2); }` jQuery(d).ready($ => { const $body = $(d.body) // スタイルをあてる $body.append($('<style>').addClass(userScriptId).text(style)) const urlObj = new URL(location.href) const imageExtSelectors = settings.imgExts.map(ext => `a[href$=".${ext}"]`).join(',') const imageStyle = {height: size, width: size, objectFit: 'contain'} const posts = $('.post,dd').get() // ボード名など抽出 const urlMtc = location.href.match(/https?:\/\/(.+\.5ch\.net)\/test\/read.cgi\/(\w+)\/.*/) const boardName = urlMtc?.[2] const boardTopHref = urlMtc ? `//${urlMtc[1]}/${urlMtc[2]}` : '' // 以下、独自ナビバー関連機能 $('.container.container_body').arrive('.contents', contents => { const $nav = $('<nav>', {id: `${userScriptId}`}) const $contents = $(contents) $contents.prepend($nav) $nav.html(` <a href="${boardTopHref}">板Top</a> <a href="/${boardName}/subback.html">スレッド一覧<a> <a id="${userScriptId}extract_im" class="link">画像スレ抽出</a> <div class="related-thread"> <a class="dropdown-toggle" href="#" id="dropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">関連スレ</a> <ul id="${userScriptId}relations" class="dropdown-menu" aria-labelledby="dropdownMenuLink"></ul> </div> `) $nav.prepend($contents.children('.title')) // 公式ナビバーを削除 $('nav.search-header').remove() // 画像スレだけ抽出機能 posts.forEach(post => /* 改行削除、正規化 */$(post).css({display: 'block'}).next('br').remove()) const $notImgPosts = $(posts.filter(post => { const $post = $(post) return !$post.find('.image,img')[0] && !$post.find('a').get().some(a => isImageUrl($(a).attr('href'))) })) let flag = false const showImgPost = bool => $notImgPosts.css({display: bool ? 'block' : 'none'}) $(`#${userScriptId}extract_im`).click(() => { showImgPost(flag) flag = !flag }) // 関連スレURL候補 抽出 const regMain = /(https:.+?)\/(\d+)(\/[\d-]+)?/ const mainLocationHref = location.href.match(regMain)?.[1] if (mainLocationHref) { const relationHrefs = posts.flatMap(post => { return $(post).find('.escaped').find('a').get().map(a => { const href = a.getAttribute('href') const idx = href.indexOf(mainLocationHref) return idx < 0 ? undefined : href }).filter(Boolean) }) utilUnion(relationHrefs).forEach(relationHref => { let url try { url = new URL(relationHref) } catch (e) { console.warn(e) } const txt = relationHref.match(regMain)?.[2] ?? url.pathname ?? relationHref const $li = $('<li>').addClass('dropdown-item') const a = $('<a>').attr({ href: relationHref, target: '_blank' }).text(txt) $li.append(a).appendTo(`#${userScriptId}relations`) }) } })// 以上、独自ナビバー関連機能 // 書き込みボタンを強調表示 $(".submitbtn.btn").addClass('btn btn-primary') // プレビュー画面を用意 const $thread = $('.container.container_body') $thread.wrapInner($('<div>').addClass('contents')) const $sideContainer = $('<div>').addClass('side-container') const $settingsWin = $('<div>').addClass('settings-win').appendTo($sideContainer).html(`画像プレビュー`) const $previewImgBox = $('<div>').addClass('preview-img-box').appendTo($sideContainer) $thread.append($sideContainer) // プレビュー関数 const addPreview = imgUrl => $previewImgBox.append($('<img>', {src: imgUrl})) const removePreview = () => $previewImgBox.empty() $(d).on('keydown', e => { if (settings.keys.removePreview.includes(e.key)) removePreview() }) // 曖昧リンクを認識 $('.post .escaped,dd').get().forEach(txtField => { const nodes = $(txtField).contents().get().filter(node => node.nodeType === 3).forEach(txtNode => { const $txtNode = $(txtNode) const txt = $txtNode.text() const mt = txt.match(/(\S*?)(\/\/\S+)/) if (!mt) return const isHttp = mt[1].lastIndexOf('p:') == mt[1].length - 2 $txtNode.after($('<a>', {href: `http${isHttp ? '' : 's'}:${mt[2]}`, target: '_blank'}).text(txt)) $txtNode.remove() }) }) const $links = $('.escaped a,dd a') const $imageLinks = $(`a.image,${imageExtSelectors}`) // リンク描画時 $links.get().forEach(link => { const $link = $(link) const href = optionalHttps(remove5chJump($link.attr('href'))) if (typeof href !== 'string') return const patterns = [/^.+:\/\/(?:www|m)\.youtube\.com.*?v=([\w-=]+).*$/, /^.+:\/\/youtu\.be\/([\w-=]+).*$/, /^.+:\/\/www\.youtube\.com\/embed\/([\w-=]+).*$/] let match for (const pattern of patterns) { const mt = href.match(pattern) if (mt) { match = mt; break } } if (!match) { // 5ch外部サイト中継ページ無効化 if (!isImageUrl(href)) $link.attr('href', href) return } // 以降 YouTube iframe 生成 $link.text('') $link.after( $('<iframe>', {src: `https://www.youtube.com/embed/${match?.[1]}?controls=1`}) .attr({frameborder: 0, allowfullscreen: ''}) .css({width: '40rem', height: '22.5rem'}) ) }) // 小サイズ画像の見た目の処理 const previewImgByHover = $img => { $img.on('mouseover', ({target}) => {removePreview(); addPreview($(target).attr('src'))}) } $imageLinks.get().forEach(imageLink => { const $imageLink = $(imageLink) setTimeout(() => { if ($imageLink.children('[div="thumb5ch"]')[0]) return // 小サイズ画像が生成されてなかったら const imgUrl = optionalHttps(remove5chJump($imageLink.attr('href'))) if (!imgUrl) return const $addImg = $('<img>', {src: imgUrl, loading: 'lazy'}).css(imageStyle) $imageLink.after($addImg) previewImgByHover($addImg) }, 2000) $imageLink.children('div').css({display: 'inline', width: 'initial'}) // 改行を削除 $imageLink.next('br').remove() }) // マウスホバーしたら画像をプレビュー let previewImgUrl $imageLinks.on('click', e => e.preventDefault()) $imageLinks.on('mouseover', ({currentTarget}) => { removePreview() const imgUrl = optionalHttps(remove5chJump($(currentTarget).attr('href'))) if (!imgUrl) return previewImgUrl = imgUrl addPreview(imgUrl) }) // リプライレスに画像プレビューを適応 $body.arrive('[id^="reply"]', el => previewImgByHover($(el).find('img'))) // Dキー押下でプレビュー画像ダウンロード $(d).on('keydown', e => { if (!settings.keys.download.includes(e.key) || !previewImgUrl) return GM_download({url: previewImgUrl, name: previewImgUrl.replace(/^.+\//, ''), onerror: console.warn}) }) }) })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址