您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Repairs embeds in your own and your friend soup. Fixes embeds everywhere if there is a link in the description
// ==UserScript== // @name soup.io: EmbedFix // @namespace http://xcvbnm.org/ // @author Nordern // @description Repairs embeds in your own and your friend soup. Fixes embeds everywhere if there is a link in the description // @version 0.2.1 // @match http://*.soup.io/* // @match https://*.soup.io/* // @exclude http://www.soup.io/frames/* // @exclude http://www.soup.io/remote/* // @license public domain, MediaEmbed has MIT Licence // @run-at document-end // ==/UserScript== // Available on github under: https://github.com/edave64/souplements/blob/master/youtube-fix/ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ /** * This code snippet allows you to intercept the loading of new elements and modify the loaded posts. * The basic idea is to allow filtering out posts before they are inserted into the dom, and thus before their assets * are loaded. This reduces stress on both the client and the asset servers. * * To use the filter, register the event "processBatch" in SOUP.Endless. * Example: * * SOUP.Endless.on("processBatch", function (doc) { * // your code here * }); * * The doc argument represents a temporary HTMLDocument node, storing the new loaded posts. You can work with it * like you can work with ''document''. * * Be careful to never remove all posts, otherwise Soup will assume you reached the end. * * Please keep in mind that soup already has some filters build in: http://faq.soup.io/post/4328678 * These are probably easier on the servers. * * Licence: Public domain * * UPDATE 1.1: * The soup event-api was used! I just used it wrong. It is supposed to be a template. The event is now fired on * SOUP.Endless instead of SOUP.Events * * UPDATE 1.2: * Also trigger for loaded in reactions */ (function () { // Add events to SOUP.Endless if (!SOUP.Endless.trigger) { SOUP.tools.extend(SOUP.Endless, SOUP.Events); } if (Ajax.Request._EndlessFilter) return; var oldRequest = Ajax.Request; function getLoadAboveURL() { var url = $("endless_top_post").href; return url.match(/[&?]newer=1/) ? url : url + (url.indexOf("?") >= 0 ? "&" : "?") + "newer=1"; } function getLoadBelowURL() { return SOUP.Endless.next_url.replace(/&?newer=1&?/g, ""); } function catchBatchLoad (path, options) { var oldSuccess = options.onSuccess; options.onSuccess = function (response) { var text = response.responseText, pipePosition = text.indexOf("|"), nextPath = text.substring(0, pipePosition), content = text.substring(pipePosition + 1), parser = new DOMParser(), xmlDoc = parser.parseFromString(content, "text/html"), root = xmlDoc.body; root.setAttribute("id", "posts"); SOUP.Endless.trigger("processBatch", xmlDoc); response.responseText = nextPath + "|" + root.innerHTML; return oldSuccess.apply(this, arguments); }; return oldRequest.apply(this, arguments); } function catchPreviewLoad (path, options) { var oldSuccess = options.onSuccess; options.onSuccess = function (response) { var content = response.responseText, parser = new DOMParser(), xmlDoc = parser.parseFromString(content, "text/html"), root = xmlDoc.body; root.setAttribute("id", "posts"); SOUP.Endless.trigger("processBatch", xmlDoc); response.responseText = root.innerHTML; return oldSuccess.apply(this, arguments); }; return oldRequest.apply(this, arguments); } Ajax.Request = function (path, options) { var aboveURL = getLoadAboveURL(); var belowURL = getLoadBelowURL(); if (path === aboveURL || path === belowURL) { return catchBatchLoad.apply(this, arguments); } if (path.startsWith("http://" + document.location.host + "/preview/")) { return catchPreviewLoad.apply(this, arguments); } return oldRequest.apply(this, arguments); }; Ajax.Request._EndlessFilter = true; Ajax.Request.Events = oldRequest.Events; Ajax.Request.prototype = oldRequest.prototype; }()); },{}],2:[function(require,module,exports){ require("../endlessFilter"); const MediaEmbedder = require("media-embedder"); if (!SOUP.EmbedFix) { SOUP.EmbedFix = true; function fixAll (doc) { var video_posts = [].slice.call(doc.getElementsByClassName("post_video")); const firstPost = document.querySelector(".post .content"); let width = 500; if (firstPost) { width = parseInt(window.getComputedStyle(firstPost).width); } const height = (width / 16 * 9)|0; for (const video_post of video_posts) { const embed = video_post.getElementsByClassName("embed")[0]; if (embed.children.length === 0) { let mediaData; const textarea = video_post.querySelector("[name='post[embedcode_or_url]']"); if (textarea) { mediaData = MediaEmbedder.detect(textarea.childNodes[0] ? textarea.childNodes[0].nodeValue : ""); } if (!mediaData) { const description = video_post.getElementsByClassName("body")[0]; if (description) { mediaData = MediaEmbedder.detect(description.innerHTML); } } if (mediaData) { mediaData.width = width; mediaData.height = height; embed.innerHTML = MediaEmbedder.buildIframe(mediaData); } } } } SOUP.Endless.on("processBatch", function (doc) { fixAll(doc); }); fixAll(document); } },{"../endlessFilter":1,"media-embedder":4}],3:[function(require,module,exports){ module.exports = { parse: function (text) { const a = document.createElement("a"); a.href = text; a.query = a.search.substring(1); return a; } }; },{}],4:[function(require,module,exports){ (function (global){ "use strict"; /** * @typedef MediaInfo * @name MediaInfo * @type {object} * @property {string} platform - The name of the media platfrom. * @property {string} mediaid - A string uniquely identifying on piece of media on the platform * @property {number} [height] - The height of the embeded player * @property {number} [width] - The width of the embeded player * @property {boolean} [allowFullscreen] - True if the player can enter fullscreen * @property {boolean} [loop] - True if the player will start over at the end * @property {number} [timestamp] - The number of seconds at the begining that will be skiped */ /** * @typedef MediaPlatform * @name MediaPlatform */ /** * Parses a text can either be a url to a video on a platform, or an html * snipplet containing an embedding code for one of these platforms. * * It attemps to extract as much information as possible from this text. * * @method MediaPlatform~detect * @param {string} test - A URL or an html snipplet containing an embed code * @returns {MediaInfo|undefined} Information found in the string, undefined if none where found. */ /** * Generates a iframe embed html snipplet from a descriptor * * @method MediaPlatform~buildIframe * @param {MediaInfo} descriptor * @returns {string} An html snipplet */ /** * Generates a link url from a descriptor * * @method MediaPlatform~buildLink * @param {MediaInfo} descriptor * @returns {string} A url */ /** @type {Object.<string, MediaPlatform>} */ const platforms = { youtube: require("./platforms/youtube"), dailymotion: require("./platforms/dailymotion"), vimeo: require("./platforms/vimeo") }; /** @type {MediaPlatform} */ global.test = module.exports = { detect: function(text) { for (const platform in platforms) { const ret = platforms[platform].detect(text); if (ret) { ret.platform = platform; return ret; } } }, buildIframe: function (descriptor) { const platform = platforms[descriptor.platform]; return platform.buildIframe(descriptor); }, buildLink: function (descriptor) { const platform = platforms[descriptor.platform]; return platform.buildLink(descriptor); } }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./platforms/dailymotion":6,"./platforms/vimeo":7,"./platforms/youtube":8}],5:[function(require,module,exports){ "use strict"; const querystring = require("querystring"); const UrlHelper = require("./utils/url_helper"); const DocHelper = require("./utils/doc_helper"); /** * @callback processor * @param {UrlObject} url * @param {Object} queryObject * @return {MediaInfo} */ /** * @callback generator * @param {MediaInfo} mediaInfo * @param {boolean} embedded * @param {string[]} queryParts * @returns {string} Url */ /** * @param {processor} urlProcessor * @param {generator} urlGenerator * @returns {MediaPlatform} */ module.exports = function (urlProcessor, urlGenerator) { function wrappedProcessor (text) { try { const urlData = UrlHelper.parse(text); const qs = querystring.parse(urlData.query); return urlProcessor(urlData, qs); } catch (e) { console.error(e); } } function wrappedGenerator (data, embed) { const queryData = []; queryData.when = function (condition, val) { if (condition) this.push(val); }; let url = urlGenerator(data, embed, queryData); if (queryData.length > 0) { url += "?" + queryData.join("&"); } return url; } return { detect: (text) => { // is entire text a url? let entire = wrappedProcessor(text), ret; if (entire) { return entire; } for (const tag of DocHelper.get_nodes(text, "iframe", "embed", "a")) { switch (tag.nodeName.toLowerCase()) { case "iframe": case "embed": ret = DocHelper.processIframe(tag, wrappedProcessor); if (ret) { return ret; } break; case "a": ret = wrappedProcessor(tag.getAttribute("href")); if (ret) { return ret; } break; } } }, buildIframe: (data) => { return DocHelper.buildIframe(wrappedGenerator(data, true), data.height, data.width, data.allowFullscreen); }, buildLink: (data) => { return wrappedGenerator(data, false); } }; }; },{"./utils/doc_helper":9,"./utils/url_helper":10,"querystring":13}],6:[function(require,module,exports){ "use strict"; const urlParse = /^\/(embed\/video|video|swf)\/([0-9a-zA-Z]*)/ module.exports = require("../platform_base")( function (urlData, qs) { if (urlData.normalizedHost === "dailymotion.com") { const urlMatch = urlData.pathname.match(urlParse); if (urlMatch) { return { mediaid: urlMatch[2], height: null, width: null, allowFullscreen: null, loop: null, timestamp: qs.start || null, autoplay: qs.autoplay === "1" || qs.autoPlay === "1" } } } }, function (data, embed, query) { let url = embed ? "https://www.dailymotion.com/embed/video/" : "https://www.dailymotion.com/video/"; url += data.mediaid.replace(/[^0-9a-zA-Z]/g, ""); // sanitize mediaid query.when(data.allowFullscreen === false, "fullscreen=1"); query.when(data.autoplay , "autoplay=1"); query.when(data.timestamp , "start=" + parseInt(data.timestamp)); return url; } ); },{"../platform_base":5}],7:[function(require,module,exports){ "use strict"; const urlParse = /^\/(video\/)?([0-9]*)$/; /** * @param {string} videoId * @param {object} param * @returns {MediaInfo} */ function generate (videoId, param) { return { mediaid: videoId, height: null, width: null, timestamp: null, allowFullscreen: null, loop: param.loop === "1", autoplay: param.autoplay === "1" } } module.exports = require("../platform_base")( function (urlData, qs) { if (urlData.normalizedHost === "vimeo.com" || urlData.normalizedHost === "player.vimeo.com") { if (urlData.pathname === "/moogaloop.swf") { return generate(qs.clip_id, qs); } const urlMatch = urlData.pathname.match(urlParse); if (urlMatch) { return generate(urlMatch[2], qs); } } }, function (data, embed, query) { let url = embed ? "https://player.vimeo.com/video/" : "https://vimeo.com/"; url += data.mediaid.replace(/[^0-9]/g, ""); // sanitize mediaid query.when(data.loop, "loop=1"); query.when(data.autoplay, "autoplay=1"); return url; } ); },{"../platform_base":5}],8:[function(require,module,exports){ "use strict"; const querystring = require("querystring"); const embedUrlParse = /^\/(embed|v)\/([0-9a-zA-Z\-_]*)(.*)/; /** * @param {string} videoId * @param {object} param * @returns {MediaInfo} */ function generate (videoId, param) { return { mediaid: videoId, height: null, width: null, allowFullscreen: param.fs !== "0", timestamp: param.t || param.time || param.start || null, loop: param.loop === "1", autoplay: param.autoplay === "1" } } module.exports = require("../platform_base")( function (urlData, qs) { if (urlData.normalizedHost === "youtube.com") { if (qs.v) { return generate(qs.v, qs); } const embedUrlMatch = urlData.pathname.match(embedUrlParse); if (embedUrlMatch) { // youtube /v/ urls can be kind of odd and and append the query with & to the path const vUrlQs = querystring.parse(embedUrlMatch[3]); return generate(embedUrlMatch[2], Object.assign({}, qs, vUrlQs)); } } else if (urlData.normalizedHost === "youtu.be") { const qs = querystring.parse(urlData.query); return generate(urlData.pathname.slice(1), qs); } }, function (data, embed, query) { let url = embed ? "https://www.youtube.com/embed/" : "https://www.youtube.com/watch"; const mediaid = data.mediaid.replace(/[^0-9a-zA-Z\-_]/g, ""); // sanitize mediaid query.when(data.allowFullscreen === false, "fs=1"); query.when(data.loop, "loop=1"); query.when(data.autoplay, "autoplay=1"); if (data.timestamp) { query.push("start=" + data.timestamp.replace(/[^0-9hms]/g, "")); } if (embed) { url += mediaid; } else { query.push("v=" + mediaid) } return url; } ); },{"../platform_base":5,"querystring":13}],9:[function(require,module,exports){ const DOMParser = (window.window).DOMParser; module.exports = { get_nodes: function (text, ...node_types) { let parser = (new DOMParser ()).parseFromString("<html><body>" + text + "</body></html>", "text/html"); var tags = []; for (const node_type of node_types) { tags.push.apply(tags, parser.getElementsByTagName(node_type)); } return tags; }, processIframe: function (iframe, urlProcessor) { const ret = urlProcessor(iframe.getAttribute("src")); if (ret) { ret.allowFullscreen = iframe.getAttribute("allowfullscreen") != null; ret.height = iframe.getAttribute("height"); ret.width = iframe.getAttribute("width"); return ret; } }, buildIframe: function (src, height, width, allowFullscreen) { let ret = "<iframe " if (height != null) { ret += 'height="' + parseInt(height) + '" ' } if (width != null) { ret += 'width="' + parseInt(width) + '" ' } if (allowFullscreen === true) { ret += 'allowfullscreen webkitallowfullscreen mozallowfullscreen ' } ret += 'frameborder="0" src="' + src + '"></iframe>' return ret; } }; },{}],10:[function(require,module,exports){ const url = require("url"); const querystring = require("querystring"); module.exports = { parse: function (text) { let fullUrl = url.parse(text); if (fullUrl.protocol === null) { fullUrl = url.parse("test:" + (text.startsWith("//") ? "" : "//") + text); } if (fullUrl.host) { fullUrl.normalizedHost = fullUrl.host.startsWith("www.") ? fullUrl.host.substring(4) : fullUrl.host; } return fullUrl; } }; },{"querystring":13,"url":3}],11:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; // If obj.hasOwnProperty has been overridden, then calling // obj.hasOwnProperty(prop) will break. // See: https://github.com/joyent/node/issues/1707 function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } module.exports = function(qs, sep, eq, options) { sep = sep || '&'; eq = eq || '='; var obj = {}; if (typeof qs !== 'string' || qs.length === 0) { return obj; } var regexp = /\+/g; qs = qs.split(sep); var maxKeys = 1000; if (options && typeof options.maxKeys === 'number') { maxKeys = options.maxKeys; } var len = qs.length; // maxKeys <= 0 means that we should not limit keys count if (maxKeys > 0 && len > maxKeys) { len = maxKeys; } for (var i = 0; i < len; ++i) { var x = qs[i].replace(regexp, '%20'), idx = x.indexOf(eq), kstr, vstr, k, v; if (idx >= 0) { kstr = x.substr(0, idx); vstr = x.substr(idx + 1); } else { kstr = x; vstr = ''; } k = decodeURIComponent(kstr); v = decodeURIComponent(vstr); if (!hasOwnProperty(obj, k)) { obj[k] = v; } else if (isArray(obj[k])) { obj[k].push(v); } else { obj[k] = [obj[k], v]; } } return obj; }; var isArray = Array.isArray || function (xs) { return Object.prototype.toString.call(xs) === '[object Array]'; }; },{}],12:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; var stringifyPrimitive = function(v) { switch (typeof v) { case 'string': return v; case 'boolean': return v ? 'true' : 'false'; case 'number': return isFinite(v) ? v : ''; default: return ''; } }; module.exports = function(obj, sep, eq, name) { sep = sep || '&'; eq = eq || '='; if (obj === null) { obj = undefined; } if (typeof obj === 'object') { return map(objectKeys(obj), function(k) { var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; if (isArray(obj[k])) { return map(obj[k], function(v) { return ks + encodeURIComponent(stringifyPrimitive(v)); }).join(sep); } else { return ks + encodeURIComponent(stringifyPrimitive(obj[k])); } }).join(sep); } if (!name) return ''; return encodeURIComponent(stringifyPrimitive(name)) + eq + encodeURIComponent(stringifyPrimitive(obj)); }; var isArray = Array.isArray || function (xs) { return Object.prototype.toString.call(xs) === '[object Array]'; }; function map (xs, f) { if (xs.map) return xs.map(f); var res = []; for (var i = 0; i < xs.length; i++) { res.push(f(xs[i], i)); } return res; } var objectKeys = Object.keys || function (obj) { var res = []; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key); } return res; }; },{}],13:[function(require,module,exports){ 'use strict'; exports.decode = exports.parse = require('./decode'); exports.encode = exports.stringify = require('./encode'); },{"./decode":11,"./encode":12}]},{},[2]);