Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // javascript:void function(){"use strict";(function(a){let b=function(a){return new Date(1e3*+(a=0>a?0:a||0)||0).toISOString().slice(11,19+(a===(0|a)?0:4)).split(":").map((b,c)=>+b||!a&&1<c?`${+b}${c?1<c?"s":"m":"h"}`:"").join("")},c=function(a){return null!=a&&(a=(a+"").replace(/[^hms.\d]/g,"")).length?(a.replace(/^[^s]*[.\d]+$/,"$&s").match(/(?:([.\d]+)h)?(?:([.\d]+)m)?(?:([.\d]+)s)?/)||[]).slice(1).reduce((a,b,c)=>a+(b*Math.pow(60,2-c)||0),0):null},d="unauthorized",e="youtube",f=function(a){let b=a&&`${new URL(a).hostname}:`.split(":")[0]||"";switch([`${d}.tv`,`${e}.com`].findIndex(function(a){return b.endsWith(a)})){case 0:return"string"==typeof urlProtocol&&d;case 1:return e;}},g=function(a){switch(f(a)){case d:{let b=a&&new URL(a),c="object"==typeof uatvPlayer&&uatvPlayer,d=c&&contentId,e=d&&c.currentTime(),f=b&&b.pathname.match(/\b((watch|video)|(listen))\b/)||[],g=f[2]?"/watch":f[3]?"/listen":null;return g&&[d,e,g]}case e:{let b=a&&new URL(a),c=b&&(b.searchParams.get("v")||(b.pathname.match(/^\/(?:shorts|video|watch|live)\/([^\/?&]+)/)||[])[1]),d=c&&document.querySelector("video"),e=d&&d.currentTime;return d&&[c,e,d&&"/watch",(b.search.match(/(&list=[^&]+)(&index=\d+)?/)||[])[0]]}}},h=function(a,c,g,h,i){switch(f(a)){case d:return`${urlProtocol}//${urlDomain}${g}/${c}/s${h}/`;case e:return`${a.origin}${g}?v=${c}${i||""}&t=${b(h)}`;}},i=", ; : ' -";return function(a,d){let e=window.location,[j,k,l,m]=g(e)||[],n=b(null==a?k||0:0>a?0:a),o=`\n${f(e)}${l} &t = ##h ##m ##s\n\nTo open in NEW window, include any of these characters: ${i}\n`,p=`\nTo open in NEW window, include any of these characters: ${i}\n\n${f(e)}${l} &t = ##h ##m ##s\n`,q=l?prompt(o,`t=${n}`):null,r=RegExp("["+i.replace(/ /,"")+"]").test(q+""),s=c(q),t=l&&h(e,j,l,s,m);console.log("%c%s%c\tjumpToTimecode(prompt default: %c%s%c) => [newSeconds = %c%s%c]\n%o%s","color:green",new Date,"","color:blue",n,"","color:red;font-weight:700",s,"",{redirectTo:t},null==s?" (aborted)":""),t&&null!=s&&!d&&setTimeout(function(){r?window.open(t):e.assign(t)},2000)}(a)})()}();
- // videoJump.js - BOOKMARKLET to easily jump to Time Code like "1h20s" (esp. helpful for MOBILE)
- // https://pastebin.com/n2635gsi pasted by LooseStool (https://libertylinks.io/LooseStool) on 29Jul2022 @ 111pm
- // e.g. https://unauthorized.tv/watch/483471/s5060/ or https://unauthorized.tv/video/483471/s5060/
- // 27Apr2023: BUGFIX YouTube '/live/id' URLs; timecodeToSeconds (and secondsToTimecode) allows '3.6' not just '3.0' AND will auto-suffix 's' if ending with just #; '.' removed from CHARS_OPEN_IN_NEW_WINDOW
- // 03Sep2022: BUGFIX in getVideoInfo() so now actually works for YouTube videos using these URLs: /shorts/id and /video/id and /watch/id ( instead of the normal /watch?v=id )
- // 14Aug2022: inside the prompt() you can enter an extra character ( any of , . ; : ' - ) to say "open in new window".
- // Also, now it works not just with UATV but with YOUTUBE too -- including /shorts/ and inside playlists.
- // https://www.youtube.com/watch?v=ufHIk5W_JBo&list=PLVuv8qtX5I89QNpuK5xI2tFaK2I0Pa43z&index=1&t=61s
- // https://www.youtube.com/watch?v=ufHIk5W_JBo&list=PLVuv8qtX5I89QNpuK5xI2tFaK2I0Pa43z&t=61s
- // https://www.youtube.com/watch?v=ufHIk5W_JBo&t=61s
- // https://www.youtube.com/shorts/4oe-Cy_dIQk
- // https://www.youtube.com/watch/4oe-Cy_dIQk
- // https://www.youtube.com/video/4oe-Cy_dIQk
- // and, of course, mobile URLs too: https://m.youtube.com/watch?v=ufHIk5W_JBo&list=PLVuv8qtX5I89QNpuK5xI2tFaK2I0Pa43z&index=1&t=61s
- // https://m.youtube.com/watch?v=ufHIk5W_JBo&list=PLVuv8qtX5I89QNpuK5xI2tFaK2I0Pa43z&t=61s
- // https://m.youtube.com/watch?v=ufHIk5W_JBo&t=61s
- // https://m.youtube.com/shorts/4oe-Cy_dIQk
- // https://m.youtube.com/watch/4oe-Cy_dIQk
- // https://m.youtube.com/video/4oe-Cy_dIQk
- "use strict";
- let videoJump = function(secondsDefault) {
- // returns timecode string "##h##m##s" from int seconds ("0s" if fails); using Date(1000 * seconds).toISOString()...map() allows us to remove "0h" or "0m" or "0s", unlike this: `${seconds = Number(seconds), Math.floor(seconds / 3600)}h${Math.floor(seconds % 3600 / 60)}m${Math.floor(seconds % 3600 % 60)}s`
- let secondsToTimecode = function(seconds) {
- // return ( new Date( 1000 * Number( seconds = (seconds < 0 ? 0 : seconds | 0 ) ) || 0 ) )
- return (
- new Date(
- 1000 * Number(
- seconds = ( seconds < 0 ? 0 : seconds || 0 )
- ) || 0
- )
- ) // || 0 instead of | 0 -- because why not allow for "3.6s" :-)
- // .toISOString().slice(11, 19).split(":") // oh yeah THIS is "why not": (3) ['00', '00', '03'] instead of (3) ['00', '00', '03.6']
- .toISOString()
- .slice(11, 19 + (
- seconds === (seconds | 0) ? 0 : 4
- ) ) // ^ so (3.6) => ['00', '00', '03.600'] , but (3.0) => ['00', '00', '03']
- .split(":")
- .map(
- (n, i) => (
- +n || (!seconds && i > 1)
- ? `${+n}${i ? i > 1 ? "s" : "m" : "h"}`
- : ""
- )
- )
- .join("");
- }
- // returns int seconds from timecode string (null if fails); very forgiving when parsing by first removing everything except [0..9, h, m, s]
- , timecodeToSeconds = function(string) {
- return string == null || !(
- string = String(string)
- .replace(/[^hms.\d]/g, "") // mainly to remove "t=" prefix, and any spaces etc. But makes sense to nuke ALL except h m s . \d
- ).length
- ? null
- : (
- string
- .replace(/^[^s]*[.\d]+$/, "$&s")
- // ^ convenience syntax allowed: ending with just "###" = presumed "###s" (unless already "s")
- // so " 3 6 " => "36s" = 36, but " 3 s 6 s " => "3s6s" = 3, " 3 s 6 " => "3s6" = 3, " 3 . 6 m " => 216, and " " => "" = 0
- .match(/(?:([.\d]+)h)?(?:([.\d]+)m)?(?:([.\d]+)s)?/)
- || []
- ).slice(1)
- .reduce(
- (c, n, i) => (
- c + (
- n * Math.pow(60, 2 - i)
- || 0
- )
- ),
- 0
- );
- }
- , UA = "unauthorized"
- , YT = "youtube"
- , getSite = function(loc) {
- let hostname = loc && `${new URL(loc).hostname}:`.split(":")[0] || "";
- switch( [`${UA}.tv`, `${YT}.com`].findIndex( function(el){ return hostname.endsWith(el); } ) ) {
- case 0: return typeof urlProtocol === "string" && UA;
- case 1: return YT;
- };
- }
- , getVideoInfo = function(loc) { // [videoId, currentTime, action, playlistInfo] = getVideoInfo(loc) || []
- switch(getSite(loc)) {
- case UA: {
- let url = loc && new URL(loc)
- , video = typeof uatvPlayer === "object" && uatvPlayer
- , videoId = video && contentId
- , currentTime = videoId && video.currentTime()
- , matches = url && url.pathname.match(/\b((watch|video)|(listen))\b/) || []
- , action = matches[2] ? "/watch" : matches[3] ? "/listen" : null;
- return action && [ videoId, currentTime, action ];
- }
- case YT: {
- // (for debugging) let url = loc && new URL(loc.href.replace("www.you", "m.you").replace(/\/(shorts|video|watch)\//g, "/watch/"))
- let url = loc && new URL(loc)
- , videoId = url && ( url.searchParams.get("v") || (url.pathname.match(/^\/(?:shorts|video|watch|live)\/([^\/?&]+)/) || [])[1] )
- // test case: https://m.youtube.com/live/CphmtP0LTek instead of https://m.youtube.com/watch?v=CphmtP0LTek
- // FAIL: , videoId = url && ( url.searchParams.get("v") || (url.pathname.match(/^\/(?:shorts|video|watch)\/([^\/?&]+)/) || [])[1] )
- // ^ WOW :blink: @ t=27m39s
- , video = videoId && document.querySelector("video")
- , currentTime = video && video.currentTime
- , action = video && "/watch";
- return video && [ videoId, currentTime, action, (url.search.match(/(&list=[^&]+)(&index=\d+)?/) || [])[0] ];
- }
- };
- }
- , getRedirectTo = function(loc, videoId, action, newSeconds, playlistInfo) {
- switch(getSite(loc)) {
- case UA: return `${urlProtocol}//${urlDomain}${action}/${videoId}/s${newSeconds}/`;
- case YT: return `${loc.origin}${action}?v=${videoId}${playlistInfo || ""}&t=${secondsToTimecode(newSeconds)}`;
- };
- }
- // , CHARS_OPEN_IN_NEW_WINDOW = ", . ; : ' -"
- , CHARS_OPEN_IN_NEW_WINDOW = ", ; : ' -"
- // always prompts (unless no 'action') and will abort if timecodeToSeconds() fails (which includes when Cancel/Escape is pressed)
- , jumpToTimecode = function(seconds, debug_LogInsteadOfRedirecting) {
- let loc = window.location
- , [videoId, currentTime, action, playlistInfo] = getVideoInfo(loc) || []
- , def = secondsToTimecode( seconds == null ? currentTime || 0 : seconds < 0 ? 0 : seconds )
- , msg = `\n${getSite(loc)}${action} &t = ##h ##m ##s\n\nTo open in NEW window, include any of these characters: ${CHARS_OPEN_IN_NEW_WINDOW}\n`
- , msg2 = `\nTo open in NEW window, include any of these characters: ${CHARS_OPEN_IN_NEW_WINDOW}\n\n${getSite(loc)}${action} &t = ##h ##m ##s\n`
- , newPlayerTime = action ? prompt(msg, `t=${def}`) : null
- , shouldOpenInNewWindow = RegExp("[" + CHARS_OPEN_IN_NEW_WINDOW.replace(/ /, "") + "]").test(String(newPlayerTime))
- , newSeconds = timecodeToSeconds(newPlayerTime)
- , redirectTo = action && getRedirectTo(loc, videoId, action, newSeconds, playlistInfo)
- , actuallyRedirect = redirectTo && newSeconds != null && !debug_LogInsteadOfRedirecting
- , REDIRECT_DELAY_MS = 2000;
- console.log("%c%s%c\tjumpToTimecode(prompt default: %c%s%c) => [newSeconds = %c%s%c]\n%o%s"
- , "color:green", new Date(), ""
- , "color:blue", def, ""
- , "color:red;font-weight:700", newSeconds, ""
- , {redirectTo}
- , newSeconds != null ? "" : " (aborted)"
- );
- actuallyRedirect && setTimeout(
- function() {
- shouldOpenInNewWindow
- ? window.open(redirectTo)
- : loc.assign(redirectTo);
- },
- REDIRECT_DELAY_MS
- );
- };
- // return jumpToTimecode(secondsDefault, true); // (for debugging)
- return jumpToTimecode(secondsDefault);
- };
- videoJump();
- /*
- https://social.infogalactic.com/micropost/b42c0b5e-e652-41f9-8973-0f3320f1b87b
- Loose Stool😶 @LooseStool a day ago
- For UATV videos (and audio) what is the URL syntax for jumping to a particular part — the equivalent of
- unauthorized.tv/watch/483471/?t=1h24m20s
- ^ Also, UATV DEVS please make the server parsing translate this syntax too!
- The Gamma | Owen's Livestreams #1454 | Unauthorized.TV
- The Gamma
- unauthorized.tv
- Roescoe @Roescoe a day ago
- unauthorized.tv/watch/483471/s5060/
- The Gamma | Owen's Livestreams #1454 | Unauthorized.TV
- The Gamma
- unauthorized.tv
- Loose Stool😶 @LooseStool a day ago
- Ah, it is /s### not /s/### (I forgot)
- But 5060 seconds is not easy math to do when casually viewing :(
- Too bad the convenient link seems to not be visible on mobile.
- Or at least allow parsing alias for: /h124m20s if the ?t ain’t possible
- Roescoe @Roescoe a day ago
- It only shows when you switch to audio and then back or vice versa. It’s not a fully fledged implementation yet. I assume eventually there will be shareable links and saved spots.
- Loose Stool😶 @LooseStool a day ago
- I noticed if you go cold it doesnt work, but if already playing then it works. Which means refreshing makes it work that second time (after reload).
- Maybe today I will throw together a quick bookmarklet as a temporary workaround for myself
- */
Advertisement
Add Comment
Please, Sign In to add comment