您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download/Share Images Faster
当前为
// ==UserScript== // @name TweetDeck Image Assistant // @namespace http://ejew.in/ // @version 0.9 // @description Download/Share Images Faster // @author EntranceJew // @match https://tweetdeck.twitter.com/* // @require https://cdn.rawgit.com/eligrey/FileSaver.js/5ed507ef8aa53d8ecfea96d96bc7214cd2476fd2/FileSaver.min.js // @require https://cdn.rawgit.com/kamranahmedse/jquery-toast-plugin/1105577ed71ef368f8aa3d96295857643dca43d7/dist/jquery.toast.min.js // @noframes // @resource toastCSS https://cdn.rawgit.com/kamranahmedse/jquery-toast-plugin/1105577ed71ef368f8aa3d96295857643dca43d7/dist/jquery.toast.min.css // @grant GM_addStyle // @grant GM_getResourceText // ==/UserScript== /* 0.9 - toast notifications, better clipboard access methods, better image sources, ctrl+click like/rt/download to follow from column owner, fixed errors in previewer 0.8 - added t.co link unmasking 0.7 - apparently getting gif sources works most reliably inside callbacks 0.6 - hotfix to prevent redundant page reloading with stream-media seek methods 0.5 - video links no longer destroy links, ctrl+click the timestamp to copy the tweet link, ctrl+click the link icon to prepare multi-image tweets for discord 0.4 - changed download icon, added copy links button, videos now don't flash their preview, videos no longer close your draft tweets panel 0.3 - gif support wasn't that hard 0.2 - removed debug prints, updated mimes, added video download link, instant-spice now grabs videos 0.1 - initial version */ (function() { 'use strict'; GM_addStyle( GM_getResourceText("toastCSS") ); var toast_prototype = { text: "Don't forget to star the repository if you like it.", // Text that is to be shown in the toast heading: 'Note', // Optional heading to be shown on the toast icon: 'success', // Type of toast icon showHideTransition: 'slide', // fade, slide or plain allowToastClose: true, // Boolean value true or false hideAfter: 1000, // false to make it sticky or number representing the miliseconds as time after which toast needs to be hidden stack: 32, // false if there should be only one toast at a time or a number representing the maximum number of toasts to be shown at a time position: 'bottom-left', // bottom-left or bottom-right or bottom-center or top-left or top-right or top-center or mid-center or an object representing the left, right, top, bottom values textAlign: 'left', // Text alignment i.e. left, right or center loader: true, // Whether to show loader or not. True by default loaderBg: '#9EC600', // Background color of the toast loader beforeShow: function () {}, // will be triggered before the toast is shown afterShown: function () {}, // will be triggered after the toat has been shown beforeHide: function () {}, // will be triggered before the toast gets hidden afterHidden: function () {} // will be triggered after the toast has been hidden }; function toast( heading, text, icon ){ return $.toast(jQuery.extend(true, toast_prototype, { heading: heading, text: text, icon: icon })); } var toolbar_size = 6; var tool_icon_width = (1 / toolbar_size) * 100; GM_addStyle( ".tweet-detail-action-item, .without-tweet-drag-handles .tweet-detail-action-item { width: " + tool_icon_width + "% !important; }" ); var tool_icon = '<li class="tweet-action-item pull-left margin-r--13 margin-l--1">'; tool_icon += '<a class="js-show-tip tweet-action position-rel" href="#" rel="download" title="" data-original-title="Download">'; tool_icon += '<i class="icon icon-attachment icon-attachment-toggle txt-center"></i> <span class="is-vishidden"> Download </span>'; tool_icon += '</a> </li>'; var link_icon = '<li class="tweet-action-item clipboard pull-left margin-r--13 margin-l--1">'; link_icon += '<a class="js-show-tip tweet-action position-rel" href="#" rel="hotlink" title="" data-original-title="Hotlink">'; link_icon += '<i class="icon icon-link icon-link-toggle txt-center"></i> <span class="is-vishidden"> Hotlink </span>'; link_icon += '</a> </li>'; var mime_db = { jpeg: "image/jpeg", jpg: "image/jpeg", gif: "image/gif", webp: "image/webp", mp4: "video/mp4", m3u8: "application/x-mpegURL", undefined: "text/plain" }; function clipboard_data( text ){ var tc = $('.compose-text-container .js-compose-text'); var orig = tc.val(); var active = document.activeElement; tc.val( text ); tc[0].focus(); tc[0].setSelectionRange( 0, text.length ); document.execCommand("copy"); tc.val( orig ); active.focus(); toast("Copied <em>" + text.split(/\r*\n/).length + "</em> Lines!", text, "info"); } // http://stackoverflow.com/a/2091331 function getQueryVariable(str, variable) { var query = str.substring(1); var vars = query.split('&'); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split('='); if (decodeURIComponent(pair[0]) == variable) { return decodeURIComponent(pair[1]); } } console.log('Query variable %s not found', variable); } function detect_mime(url){ return mime_db[ /(?:\.([^.]+))?$/.exec(url)[1] ]; } function get_img_data( url, on_load ) { var xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.responseType = "blob"; xhr.onload = on_load; xhr.send(); } function download_now( url ){ if( url.length ){ get_img_data( url, function( e ){ var img_name = url.substring( url.lastIndexOf('/')+1 ); var the_blob = new Blob([this.response], {type: detect_mime(url)}); var save_file_name = img_name.replace(/:orig$/, ""); saveAs( the_blob, save_file_name ); if( save_file_name.endsWith('mp4') ){ toast("Downloaded <em>1</em> Video!", save_file_name, "info"); } }); } } function nice_url( url, replacement ){ if( !replacement || replacement !== "" ){ replacement = ":orig"; } var bg = url; bg = bg.replace('url(','').replace(')','').replace(/\"/gi, ""); bg = bg.replace(/:thumb$/, replacement); bg = bg.replace(/:small$/, replacement); bg = bg.replace(/:medium$/, replacement); bg = bg.replace(/:large$/, replacement); return bg; } // danger: this could potentially lockup if the element isn't guaranteed to appear. function lock_find( selector, context ){ var results = $( selector, context ); while( !results.length ){ results = $( selector, context ); } return results; } // we have to do literal jungle japes in order to get to the follow button from here // strap in function follow_tweet( selector ){ selector.find('ul.tweet-actions i.icon-more').click(); var column_owner = selector.parents('.column-panel').find('h1.column-title span.attribution').text(); var more = lock_find('.js-dropdown.dropdown-menu a[data-action="followOrUnfollow"]', selector); more.parent('li.is-selectable').addClass('is-selected'); more.click(); var follow_container = lock_find('div.js-modal-panel'); var column_owner_follow = null; // entrancejew only follows from his third account // entrancejew also refuses to implement settings yet if( column_owner == "@EntranceJew" ){ column_owner_follow = lock_find('div.js-follow-from:nth-child(3)', follow_container); } else { follow_container.find('.js-from-username').each(function(){ var this_name = $( this ).text(); if( this_name.includes( column_owner ) ){ column_owner_follow = $( this ).parent('.js-follow-from'); } }); } var follow_button = null; var follow_seeker = setInterval(function(){ follow_button = column_owner_follow.find('.js-action-follow[class*=" s-"]'); if( follow_button.length ){ if( follow_button.hasClass('s-not-following') ){ var user_to_follow = $('.mdl-header-title a[rel="user"]').text(); follow_button.find('button').click(); toast("Followed <em>1</em> Users!", user_to_follow, "info"); } else if( !follow_button.hasClass('s-following') ){ var attrs = follow_button.attr('class'); toast("I'm Confused!", "What is a <em>" + attrs + "</em>?", "error"); } follow_container.find('.icon-close').click(); clearInterval(follow_seeker); } },50); } setInterval(function(){ $('.stream-item:not([data-ejew])').each(function(){ var grand_dad = $( this ); /* // for appending to the dropdown menu if we wanted that var tool_bar = grand_dad.find('.js-dropdown-content > ul'); tool_bar.prepend('<li class="is-selectable"><a href="#" data-action="ejew">Spice it up</a></li>'); */ // find all the images and store their links in data var sources = []; var media_type = 'idk'; if( grand_dad.find('.is-video').length ){ media_type = 'video'; sources.push( function( e ){ var anchor = grand_dad.find('.js-media-image-link'); var o_target = anchor.attr('target'); var o_src = anchor.attr('src'); anchor.attr('target', ''); anchor.attr('src', '#'); anchor.click(); var embeds = lock_find('.js-embeditem'); var vid_url = ''; embeds.each(function(){ var iframe_src = $( this ).find( 'iframe' ).attr('src'); if( iframe_src ){ vid_url = getQueryVariable( iframe_src, 'video_url' ); } $('.mdl-dismiss .icon-close').click(); }); anchor.attr('target', o_target); anchor.attr('src', o_src); if( vid_url.length ){ return vid_url; } }); } else if( grand_dad.find('.is-gif').length ){ media_type = 'gif'; sources.push( function(){ return grand_dad.find('video.js-media-gif').attr('src'); }); } else { grand_dad.find('.js-media-image-link, .js-media .media-image').each( function(i, el){ sources.push( nice_url( $( el ).css('background-image') ) ); }); if( sources.length ){ media_type = 'image'; } } var orig_link = grand_dad.find("a.txt-small.no-wrap[rel=\"url\"]"); orig_link.on('click', function(e){ if( e.ctrlKey ){ e.preventDefault(); clipboard_data( $( this ).attr("href") ); } }); grand_dad.data('ejew-sources', sources); grand_dad.data('direct-url', orig_link.attr("href")); // enhance stock buttons with auto-follow grand_dad.find('.icon-retweet').on('click', function(e){ if( e.ctrlKey ){ follow_tweet( grand_dad ); } }); grand_dad.find('.icon-favorite').on('click', function(e){ if( e.ctrlKey ){ follow_tweet( grand_dad ); } }); // add more buttons var new_link = $( link_icon ); new_link.on('click', function(e){ var sources = grand_dad.data('ejew-sources'); for( var i = 0; i < sources.length; i++ ){ if( typeof( sources[i] ) != "string" ){ sources[i] = sources[i]( this ); } } var the_url = grand_dad.data('direct-url'); if( e.ctrlKey && sources.length > 1){ sources[0] = the_url; } if( sources.length ){ clipboard_data( sources.join("\n") ); } else { clipboard_data( the_url ); } }); // make an instance of the toolbar button var new_tool = $( tool_icon ); new_tool.on('click', function(e){ var sources = grand_dad.data('ejew-sources'); for( var i = 0; i < sources.length; i++ ){ var source = sources[i]; if( typeof( source ) != "string" ){ source = source( this ); } download_now( source ); } if( sources.length > 1 || !sources[0].endsWith("mp4") ){ toast("Downloaded <em>" + sources.length + "</em> Images!", sources.join("\n"), "info"); } if( e.ctrlKey ){ follow_tweet( grand_dad ); } }); // attach var attachment_point = grand_dad.find('ul.tweet-actions > li:nth-last-child(2)'); attachment_point.before( new_tool ); attachment_point.before( new_link ); // prevent loading up this element again grand_dad.attr('data-ejew', 'in'); }); // unmask t.co links var links_to_unmask = $('a[href^="https://t.co/"][data-full-url]'); links_to_unmask.each(function(){ $( this ).attr('href', $( this ).data('full-url') ); }); if( links_to_unmask.length > 0 ){ toast("Unmasked <em>" + links_to_unmask.length + "</em> Links!", "<em>That's a lot!</em>", "info"); } // make it so that you can copy image source from previews $('img.media-img:not([data-ejew])').each(function(){ $( this ).attr('src', nice_url( $( this ).attr('src'), "" ) ); $( this ).attr('boners', 'farts'); $( this ).attr('data-ejew', 'in'); }); // provide a download source link in zoomable previews for videos $('.js-embeditem:not([data-ejew])').each(function(){ var iframe_src = $( this ).find( 'iframe' ).attr('src'); if( iframe_src ){ var vid_url = getQueryVariable( iframe_src, 'video_url' ); var dl_link = $( '<a href="#">Download Source</a>' ); dl_link.on('click', function(){ download_now( vid_url ); }); $(".med-origlink").after( dl_link ); } $( this ).attr('data-ejew', 'in'); }); }, 300); })();