Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save samithaj/2504460d59dca99592c6 to your computer and use it in GitHub Desktop.
Save samithaj/2504460d59dca99592c6 to your computer and use it in GitHub Desktop.
Subreddit to YouTube Source Bookmarklet - Play YouTube music from subreddits in Foobar with foo_youtube

Subreddit to YouTube Source Bookmarklet

Generates .M3U playlist of all YouTube videos in current subreddit listing for use in foobar2000 with the foo_youtube component.

First off, foo_youtube is awesome! It enables you to listen to YouTube songs in Foobar, which opens up possibilities like making playlists, enqueuing tracks, remembering playback state, quickly mixing in a song you don't have in your library, using DSPs like Channel Mixer (surround sound up-mixing from stereo works wonders!). You're also free to restart the browser, and you don't have to wonder which of the hundred tabs the sound is coming from, LOL.

How to install

  1. Copy the contents of the second file Subreddit-to-YouTubeSource.min.js

  2. Add a new bookmark

  3. Name it something like Subreddits to YouTube Source, or however you want

  4. In the URL field paste what you copied and Save

How to use

  1. Go to Reddit, pick your favorite music subreddit and look for the listing you want.

  2. Hot, new, top, try sorting. You can even add &limit=10 to the address to load 10 songs per page instead of the usual 25. Optionally if you don't wanna mess about you can edit the bookmarklet's default which is subredditLimit = 100 #L29

  3. When you're happy with the list click on the bookmarklet.

  4. A file will be generated and you'll be prompted to save it (or it will start downloading automatically).
    The filename will look like <coolsubreddit>-<hot>-<50>.m3u8.
    Optionally you may also use the console reddit.toM3U('myplaylist.m3u')

  5. Open this file with Foobar and enjoy the tunes!
    Dead videos will be replaced with a search, which will oftentimes work in the playlist like magic. However, after the search result finishes playing, another search result for the same song starts. You have to remove those manually. YouTube API v2 was deprecated, you need an API key for every call now. Dead videos will now be graciously skipped (thanks 3dyd!). If you want you can right click them individually and YouTube Source > Search replacement (manual).

Enjoy, and thank 3dyd for the component!

// Subreddits to YouTube Source
// Generates .M3U playlist of all YouTube videos in current subreddit listing
// for use in foobar2000 with foo_youtube component.
// TODO: Better playlist handling?
(function(reddit){
// Are we on reddit?
if(!window.location.hostname.match('reddit.com')) {
return;
}
// Are we on a listings page?
if(!reddit.pageInfo.actionName.match(/GET_(listing|search)/)) {
return;
}
var playlist = '#EXTM3U\n', // Required header for M3U Extended playlist format
subredditTracks = 0,
subredditName = reddit.post_site, // leeroysjams|psybient|progtrance
subredditSort = $('.tabmenu .selected').text() || 'search', // hot|top|new|rising|controversial
subredditLimitArg = window.location.search.match(/(?:limit=)(\d{1,3})/),
endParams = function() {
if (subredditLimitArg) {
subredditLimitArg = parseInt(subredditLimitArg[1]); // Grab value from URL limit parameter
return window.location.search;
} else {
subredditLimitArg = 100; // 0...100?
return '?limit=' + subredditLimitArg + '&' + window.location.search.substr(1);
}
},
subredditJSONURL = window.location.href.replace(window.location.search,'') + '.json' + endParams(),
subredditListing;
jqxhr = $.getJSON(subredditJSONURL, function(json) {
subredditListing = json.data.children; // because that's their structure
}) // end getJSON()
.done(function() {
processListing();
// Initiate the download when the AJAX is done.
reddit.toM3U();
});
function processListing() {
$.each(subredditListing, function(key, value) {
var url = value.data.url,
re = new RegExp('(?:youtube(?:-nocookie)?.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu.be/)([^"&?/ ]{11})(?:.*list=([^"&?/ ]*))?');
if (re.test(url)) {
var itemArtistTitle = value.data.title.replace(/&amp;/g, '&').split(/ - | (?=\[)/),
itemArtist = itemArtistTitle.shift(), // using [0] wouldn't change the itemArtistTitle array
itemTitle = itemArtistTitle.join(' - '),
itemURL = url.replace(/&amp;/g, '&'); // &amp; fix is cosmetic, still works okay without this
itemURL = itemURL.replace(/^https?:\/\//i, "3dydfy://");
if (itemArtist && itemTitle && /^3dydfy:\/\//.test(itemURL)) {
itemURL += '&fb2k_artist=' + encodeURIComponent(itemArtist) + '&fb2k_title=' + encodeURIComponent(itemTitle);
itemURL += '&fb2k_album=r%2F' + subredditName;
if (re.exec(url)[2]) {
// Here we produce a proper playlist URL for the Add YouTube URL menu item
var ytPlaylistID = re.exec(url)[2];
// And we populate the track's comment field with it because we can't do anything else
itemURL += '&fb2k_tracknumber=?%2F???&fb2k_comment=www.youtube.com%2Fplaylist?list=' + ytPlaylistID;
}
}
// Write the item to the .m3u8 playlist
playlist += '#EXTINF:' + itemArtist + ' - ' + itemTitle + '\n' + itemURL + '\n';
subredditTracks++;
}
}); // end $.each
}
// Enhance reddit object with awesome new method
reddit.toM3U = function(filename){
if(!filename) filename = subredditName + '-' + subredditSort + '-' + subredditTracks + '.m3u8';
var blob = new Blob([playlist], {encoding:"UTF-8",type:"text/plain;charset=UTF-8"}),
e = document.createEvent('MouseEvents'),
a = document.createElement('a');
a.download = filename;
a.href = window.URL.createObjectURL(blob);
a.dataset.downloadurl = ['text/plain', a.download, a.href].join(':');
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(e);
};
})(reddit);
javascript:(function(reddit){if(!window.location.hostname.match("reddit.com")){return}if(!reddit.pageInfo.actionName.match(/GET_(listing|search)/)){return}var playlist="#EXTM3U\n",subredditTracks=0,subredditName=reddit.post_site,subredditSort=$(".tabmenu .selected").text()||"search",subredditLimitArg=window.location.search.match(/(?:limit=)(\d{1,3})/),endParams=function(){if(subredditLimitArg){subredditLimitArg=parseInt(subredditLimitArg[1]);return window.location.search}else{subredditLimitArg=100;return"?limit="+subredditLimitArg+"&"+window.location.search.substr(1)}},subredditJSONURL=window.location.href.replace(window.location.search,"")+".json"+endParams(),subredditListing;jqxhr=$.getJSON(subredditJSONURL,function(json){subredditListing=json.data.children}).done(function(){processListing();reddit.toM3U()});function processListing(){$.each(subredditListing,function(key,value){var url=value.data.url,re=new RegExp('(?:youtube(?:-nocookie)?.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu.be/)([^"&?/ ]{11})(?:.*list=([^"&?/ ]*))?');if(re.test(url)){var itemArtistTitle=value.data.title.replace(/&amp;/g,"&").split(/ - | (?=\[)/),itemArtist=itemArtistTitle.shift(),itemTitle=itemArtistTitle.join(" - "),itemURL=url.replace(/&amp;/g,"&");itemURL=itemURL.replace(/^https?:\/\//i,"3dydfy://");if(itemArtist&&itemTitle&&/^3dydfy:\/\//.test(itemURL)){itemURL+="&fb2k_artist="+encodeURIComponent(itemArtist)+"&fb2k_title="+encodeURIComponent(itemTitle);itemURL+="&fb2k_album=r%2F"+subredditName;if(re.exec(url)[2]){var ytPlaylistID=re.exec(url)[2];itemURL+="&fb2k_tracknumber=?%2F???&fb2k_comment=www.youtube.com%2Fplaylist?list="+ytPlaylistID}}playlist+="#EXTINF:"+itemArtist+" - "+itemTitle+"\n"+itemURL+"\n";subredditTracks++}})}reddit.toM3U=function(filename){if(!filename)filename=subredditName+"-"+subredditSort+"-"+subredditTracks+".m3u8";var blob=new Blob([playlist],{encoding:"UTF-8",type:"text/plain;charset=UTF-8"}),e=document.createEvent("MouseEvents"),a=document.createElement("a");a.download=filename;a.href=window.URL.createObjectURL(blob);a.dataset.downloadurl=["text/plain",a.download,a.href].join(":");e.initMouseEvent("click",true,false,window,0,0,0,0,0,false,false,false,false,0,null);a.dispatchEvent(e)}})(reddit);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment