5
\$\begingroup\$

Aside of nursing my child, Vitsy, and dabbling in the art of code golf, I also am an avid dubstep fan, listening to all the new dubstep that Monstercat puts out as regularly as I can. I also share a random dubstep of the day, though not regularly, in the Nineteenth Byte. This last bit is what this challenge relates to; so, without further ado, let me get to what you actually need to think about:

The Challenge

You must generate a randomly sorted list of songs from this playlist in the following format:

<Title> - <Artist>: <link to the video>

Rules

  • The output list must update with the playlist provided, or every byte used counts as 2 (your score would be double your byte count).
  • The output list must have no repeats.
  • The output list must be given in the specified format.
  • The output list must have all of the videos listed in the playlist provided.
  • You may not use builtins for array shuffling (such as Collections.shuffle(List<?> x) from Java)
  • You may not use the YouTube builtin shuffle mechanism (as this is not random).
  • There are no limits on runtime.

Example Output

Division - Ephixa: https://www.youtube.com/watch?v=M1mPFWhgYUM&list=PLF5C76212C58C464A&index=95
Wanna Know You (ft. Holly Drummond) - Direct: https://www.youtube.com/watch?v=6QWQfzYMJTs&list=PLF5C76212C58C464A&index=24
Edge of the World (feat. Becko) - Razihel & Xilent: https://www.youtube.com/watch?v=P0dQ55ZgNkw&index=4&list=PLF5C76212C58C464A
... until the final video ...
Way Too Deep - Grabbitz: https://www.youtube.com/watch?v=4T28WgVqdrA&index=8&list=PLF5C76212C58C464A

It may be worth perusing the YouTube API, but parsing the page with the playlist on it might save you some time.

\$\endgroup\$
  • \$\begingroup\$ If the solution does not update with the playlist, what format is it expected to receive it in? \$\endgroup\$ – user81655 Feb 10 '16 at 10:57
  • \$\begingroup\$ @user81655 There is no input whatsoever, in either case. The link for gathering the information is constant. Hardcoding the list of songs will double your byte count. \$\endgroup\$ – Addison Crump Feb 10 '16 at 11:05
  • \$\begingroup\$ Hey, +1 for Monstercat! \$\endgroup\$ – cat Apr 9 '16 at 20:15
  • \$\begingroup\$ What counts as a builtin for shuffling? \$\endgroup\$ – cat Apr 9 '16 at 20:47
  • 1
    \$\begingroup\$ @cat When a builtin relocates items in an array or list with a single call. \$\endgroup\$ – Addison Crump Apr 9 '16 at 22:00
6
\$\begingroup\$

JavaScript, 532 536 538 582 bytes

Thanks to @ӍѲꝆΛҐӍΛПҒЦꝆ and @Quill for a whole bunch of bytes.

I did do a couple of things here which may not be 100% to the spec - If they aren't admissible please tell me and I'll fix them:

  1. I shortened the YouTube URL
  2. The shuffle is VERY weak, and is not considered in any way close to random.
  3. There's a bit of whitespace due to the join.

I'd like to start by saying, to any JavaScript developers out there: I'm so sorry.

New version alternates captures to reduce on regex byte cost and strips some URL length, but sacrifices on alerts. Now instead of 3, you get like 20. Unfortunately the https on the URL's seem to be required or the page won't load, though that may be my setup :/.

This could possibly be improved with proper XML parsing and loops instead of weird recursion things with calls to other functions. I had fun taking advantage of the fact that XMLHttpRequest causes synchronous execution.

d="https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=PLF5C76212C58C464A&key={REALLY SUPER LONG GOOGLE API KEY HERE}",h=[];o=l=>{x=new XMLHttpRequest,x.open("GET",l,!1),x.send(0),j=x.responseText;y=`nextPageToken": "`;e(j)
j.includes(y)&&(q=j.split(y)[1].split('"')[0],o(d+"&pageToken="+q));h.sort(b=>.5-Math.random());alert(h.join `
`)}
e=v=>{for(r=/(title": "(.*)\[)|(oId": "(.*)")/g;null!==(m=r.exec(v));)a=m[2]+": https://youtu.be/"+r.exec(v)[4],z="] - ",a.includes(z)&&(a=a.split(z)[1]),h.push(a)}
o(d)

Replace the {REALLY SUPER LONG GOOGLE API KEY HERE} with your 39 character long YouTube API key.

Ungolfed:

//Define base URL, sacrificing speed and pages for bytes
d = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=PLF5C76212C58C464A&key={REALLY SUPER LONG GOOGLE API KEY HERE}";
h = [];
o = l => {
    //Grab API data
    x = new XMLHttpRequest();
    x.open("GET", l, false);
    x.send(0);
    j = x.responseText;
    //Set up a split since we use it twice
    y = `nextPageToken": "`;
    //At this point, jump off to the other function to deal with the output
    e(j);
    //If there are multiple pages of results, as we only get a couple from each API call to save bytes with URL's
    if (j.includes(y)) {
        //Get the ID for the next page
        q = j.split(y)[1].split("\"")[0];
        //Call again with an added parameter
        o(d + "&pageToken=" + q)
    }
    //Taking advantage of XMLHttpRequest forcing this as synchronous, so all the above happens first
    //Bad shuffle
    h.sort(b => .5 - Math.random())
    alert(h.join `
        `);
}
e = v => {
    //Regex for titles/ID's - Grabbitz doesn't fit the "[Genre] - Song - Artist" mould so we need a slightly longer regex
    r = /(title": "(.*)\[)|(oId": "(.*)")/g;
    //Since we know there's one title to one video, we can run them both at the same time
    while ((m = r.exec(v)) !== null) {
        //Build the string
        a = m[2] + ": https://youtu.be/" + r.exec(v)[4];
        z = "] - ";
        //That Grabbitz edge case ruining my day again, I'm glad it's a good track...
        if (a.includes(z)) {
            a = a.split(z)[1];
        }
        h.push(a);
    }
}
o(d)
\$\endgroup\$
  • 2
    \$\begingroup\$ As a JavaScript developer who bears your name, I forgive you. ;) Nice post, and welcome to PPCG! \$\endgroup\$ – Conor O'Brien Feb 10 '16 at 23:41
  • \$\begingroup\$ j.indexOf(y) > -1 -> j.includes(y). "nextPageToken\"\: \"" into `nextPageToken": "` \$\endgroup\$ – Quill Feb 11 '16 at 1:14
  • \$\begingroup\$ You have some accidentally unnecessary whitespace in there. Look around arrows. \$\endgroup\$ – Mama Fun Roll Feb 11 '16 at 2:08
  • \$\begingroup\$ Also, re is a 2-character variable. Why not use R? And try keeping only m = re.exec(v) in the for loop condition. \$\endgroup\$ – Mama Fun Roll Feb 11 '16 at 2:10
  • \$\begingroup\$ Oh, change a.indexOf(z)>-1?h.push(a.split(z)[1]):h.push(a) to h.push(~a.indexOf(z)?a.split(z)[1]:a). Also, since you don't seem to be using d anywhere else in your code, try moving that string to your o call at the end. \$\endgroup\$ – Mama Fun Roll Feb 11 '16 at 2:15
4
\$\begingroup\$

Julia, 590 bytes

using Requests
L="PLF5C76212C58C464A"
u="https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=$L&key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
R(x)=JSON.parse(readall(x))
F(x)=(s=x["snippet"];(s["title"],s["resourceId"]["videoId"],s["position"]))
g=get(u)
v=[F(i)for i=R(g)["items"]]
t="nextPageToken"
while haskey(R(g),t)
g=get("$u&pageToken="*R(g)[t])
v=[v;[F(i)for i=R(g)["items"]]]
end
for j=randperm(109)
t,d,i=v[j]
t=replace(t[13:end],r"\[Mo[\w ]+\]$","")
println(join(reverse(split(t," - ")),"- "),": https://www.youtube.com/watch?v=$d&list=$L&index=$(i+1)")end

This is a full program that uses Julia's Requests module to connect to the YouTube API, get the results, and print them to STDOUT. It will not likely win unless no one else participates. The X'd out portion is my Google Developer API key, which unfortunately is 39 characters.

Ungolfed:

using Requests

# Store the playlist ID
L = "PLF5C76212C58C464A"

# Store the initial API URL
u = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=$L&key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

# Create a function for parsing JSON responses
R(x) = JSON.parse(readall(x))

# Create a function for constructing a tuple containing video information
F(x) = (s = x["snippet"]; (s["title"], s["resourceId"]["videoId"], s["position"]))

# Perform the initial API request
g = get(u)

# Collect all relevant returned information in an array
v = [F(i) for i = R(g)["items"]]

# Perform pagination to get all 109 items
t = "nextPageToken"
while haskey(R(g), t)
    g = get("$u&pageToken=" * R(g)[t])
    v = [v; [F(i) for i = R(g)["items"]]]
end

# Select the elements at random indices
for j = randperm(109)
    # Get the title, ID, and playlist index
    t, d, i = v[j]

    # Parse out the actual title
    t = replace(t[13:end], r"\[Mo[\w ]+\]$", "")

    # Print the current item
    println(join(reverse(split(t, " - ")), "- "),
            ": https://www.youtube.com/watch?v=$d&list=$L&index=$(i+1)")
end
\$\endgroup\$
3
\$\begingroup\$

PHP, 594 bytes

Here's my submission using PHP.

<?php $t=[];$n='';while(1){$l='https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=PLF5C76212C58C464A&pageToken='.$n.'&key={InsertKeyToContinueOnYourAdventure!!!}';$c=json_decode(file_get_contents($l),true);$t=array_merge($c['items'],$t);if(empty($c['nextPageToken']))break;else $n=$c['nextPageToken'];}$o=[];while(count($t)){$k=rand(0,count($t)-1);$s=$t[$k]['snippet'];echo preg_replace('/(?:\[.+\] - )?(.+) - (.+)/','${2} - ${1}',$s['title']).': https://www.youtube.com/watch?v='.$s['resourceId']['videoId'].'&list='.$s['playlistId'].'<br/>';array_splice($t,$k,1);}?>

It's probably not the best - or shortest - solution out there but it does the job. Before I go on, though, here's the ungolfed version:

<?php
$t = [];
$n = '';
while (1) {
    $l = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=PLF5C76212C58C464A&pageToken=' . $n .'&key={InsertKeyToContinueOnYourAdventure!!!}';
    $c = json_decode(file_get_contents($l), true);
    $t = array_merge($c['items'], $t);
    if (empty($c['nextPageToken']))
        break;
    else
        $n = $c['nextPageToken'];
}
$o = [];
while (count($t)) {
    $k = rand(0, count($t) - 1);
    $s = $t[$k]['snippet'];
    echo preg_replace('/(?:\[.+\] - )?(.+) - (.+)/', '${2} - ${1}', $s['title']) . ': https://www.youtube.com/watch?v=' . $s['resourceId']['videoId'] . '&list=' . $s['playlistId'] . '<br/>';
    array_splice($t, $k, 1);
}
?>

I really feel like the regular expression can be improved - both in size and quality. Also, I couldn't think of a better way to break the first while other than that ugly if-else statement. Maybe the while statement could be handled better. I don't know. It's 1:15 am here and I'm half asleep.

Looking at the other answers, it seems that PHP probably isn't ideal for this due to its long function names. Still, file_get_contents() was pretty helpful and cut down a lot of request handling. I opted to use that instead of file() since it made decoding the request easier. I also did find a way to reduce some code around around that if-else statement, but the result would throw one notice when there was no 'nextPageToken' given on the last HTTP request. I suppose if you wanted to turn error reporting off then it could be possible, but I felt like that would be cheating. For those who are curious, the ungolfed if-else would look like this:

if ($d = $c['nextPageToken']))
    break;
else
    $n = $d;

Edit: I just realised that if this were to be run from a console I could reduce the size by 11 bytes (removing php tags + replacing br/ tag with \n). Oh well, at least's more readable on a browser IMHO. I'll just leave it for now.

First code golf! Woooo! Now I must sleep. Good night, y'all!

\$\endgroup\$
  • 1
    \$\begingroup\$ Haha at least PHP throws a notice instead of outright throwing a tantrum like JS does when it can't find something to pull out of the toy pit. \$\endgroup\$ – Connor Bell Feb 11 '16 at 15:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.