|
<!DOCTYPE html> |
|
<html vocab="http://schema.org"> |
|
<head> |
|
<title>再生リストのサムネイルとプレビュー</title> |
|
<meta charset="utf-8" /> |
|
<meta name="description" content="クリックすると再生リストの中身をサムネイル表示します。サムネイルの上にマウスカーソルを重ねるとプレビュー再生できます。ChannelIDを変更すると他のチャンネルのプレイリストを表示します。" /> |
|
<meta name="keywords" content="Youtube,d3.js,Q.js,jquery" /> |
|
<meta name="author" content="sfpgmr" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> |
|
<!-- Latest compiled and minified CSS --> |
|
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"> |
|
<!-- Latest compiled and minified JavaScript --> |
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/q.js/1.0.1/q.js"></script> |
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.js"></script> |
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
|
<script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script> |
|
<script type="text/javascript" src="//www.sfpgmr.net/scripts/youtube_apikey.js"></script> |
|
<style> |
|
html { |
|
position: relative; |
|
height: 100%; |
|
} |
|
|
|
body { |
|
/* Margin bottom by footer height */ |
|
margin-top: 50px; |
|
margin-bottom: 100px; |
|
} |
|
|
|
.footer { |
|
position: absolute; |
|
bottom: 0; |
|
width: 100%; |
|
/* Set the fixed height of the footer here */ |
|
height: 60px; |
|
background-color: #f5f5f5; |
|
} |
|
|
|
div#playerObj { |
|
display: none; |
|
position: absolute; |
|
} |
|
|
|
.thumbnail-title { |
|
color: white; |
|
position: absolute; |
|
text-shadow: 1px 1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, -1px -1px 0 #000; |
|
font-size: 14px; |
|
} |
|
</style> |
|
|
|
<script type="text/javascript"> |
|
if (!location.href.match(/localhost/)) { |
|
var _gaq = _gaq || []; |
|
_gaq.push(['_setAccount', 'UA-15457703-11']); |
|
_gaq.push(['_trackPageview']); |
|
|
|
(function () { |
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; |
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; |
|
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); |
|
})(); |
|
} |
|
</script> |
|
</head> |
|
<body> |
|
<nav class="navbar navbar-default navbar-fixed-top " role="navigation"> |
|
<div class="container"> |
|
<!-- Brand and toggle get grouped for better mobile display --> |
|
<div class="navbar-header"> |
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse"> |
|
<span class="sr-only">Toggle navigation</span> |
|
<span class="icon-bar"></span> |
|
<span class="icon-bar"></span> |
|
<span class="icon-bar"></span> |
|
</button> |
|
<div><a class="navbar-brand" href="#" property="headLine" id="headLine" style="margin-top:auto;margin-bottom:auto;">Youtube API Test</a></div> |
|
</div> |
|
<div class="collapse navbar-collapse" id="navbar-collapse"> |
|
<p class="navbar-text">ChannelID</p> |
|
<form class="navbar-form navbar-left" role="search" id="channelIDForm"> |
|
<div class="form-group"> |
|
<input id="channelID" style="width:200px" type="text" class="form-control" placeholder="ChannelIDを入力"> |
|
</div> |
|
</form> |
|
<p class="navbar-text"><a id="displayPlayLists" href="#" class="navbar-link">プレイリスト一覧</a></p> |
|
<p class="navbar-text navbar-right"><a id="homeLink" href="/" class="navbar-link"><span class="glyphicon glyphicon-home"></span>ホーム</a></p> |
|
</div> |
|
</div> |
|
</nav> |
|
<article id="article" typeof="Article"> |
|
<header class="container"><h2>再生リストのサムネイルとプレビュー</h2> |
|
<p>クリックすると再生リストの中身をサムネイル表示します。サムネイルの上にマウスカーソルを重ねるとプレビュー再生できます。ChannelIDを変更すると他のチャンネルのプレイリストを表示します。</p> |
|
<h4 id="playListTitle"></h4> |
|
</header> |
|
<div class="container"> |
|
<section id="articles" class="row"></section> |
|
<aside style="margin-left:auto;margin-right:auto;display:block;"> |
|
<style> |
|
.blocks { |
|
width: 320px; |
|
height: 50px; |
|
} |
|
|
|
@media(min-width: 500px) { |
|
.blocks { |
|
width: 468px; |
|
height: 60px; |
|
} |
|
} |
|
|
|
@media(min-width: 800px) { |
|
.blocks { |
|
width: 728px; |
|
height: 90px; |
|
} |
|
} |
|
</style> |
|
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> |
|
<!-- blocks --> |
|
<ins class="adsbygoogle blocks" |
|
style="display:inline-block;text-align:center;" |
|
data-ad-client="ca-pub-4402137407996189" |
|
data-ad-slot="3288576128"></ins> |
|
<script> |
|
(adsbygoogle = window.adsbygoogle || []).push({}); |
|
</script> |
|
</aside> |
|
</div> |
|
|
|
</article> |
|
<footer id="footer" class="navbar navbar-default navbar-fixed-bottom"> |
|
<div class="container"> |
|
<div class="row"> |
|
<div class="col-xs-3" property="creator" id="creator"> |
|
<div typeof="person" id="creatorPerson"> |
|
<span property="name" id="creatorName">By <a href="http://www.sfpgmr.net/">S.F.</a></span> |
|
</div> |
|
</div> |
|
<div class="col-xs-3 text-center"></div> |
|
<div class="col-xs-6 text-right"><small><time id="date" property="dc:date" datetime="2015-01-12T19:19:52.381Z">2015-01-12T19:19:52.381Z</time></small></div> |
|
</div> |
|
</div> |
|
</footer> |
|
<div id="playerObj"> |
|
<div id="player"> |
|
</div> |
|
</div> |
|
<script type="text/javascript"> |
|
/// <reference path="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.js" /> |
|
"use strict"; |
|
|
|
var player; |
|
var playerWidth = 640; |
|
var playerHeight = 360; |
|
d3.select('head').append('script').attr('src', 'http://www.youtube.com/iframe_api'); |
|
if (window.location.href.match(/bl\.ocks\.org/i)) { |
|
d3.select('#homeLink').attr('href', 'http://bl.ocks.org/sfpgmr/'); |
|
} |
|
|
|
d3.select('#channelID').node().value = 'UCgwM0kBBsDRMZDhhWuTNR-g'; |
|
|
|
d3.select('#displayPlayLists').on('click', function () { |
|
d3.event.preventDefault(); |
|
doPlayLists(d3.select('#channelID').node().value); |
|
}); |
|
|
|
d3.select('#channelIDForm').on('submit', function () { |
|
d3.event.preventDefault(); |
|
doPlayLists(d3.select('#channelID').node().value); |
|
}); |
|
|
|
function onYouTubeIframeAPIReady() { |
|
player = new YT.Player('player', { |
|
width: playerWidth, |
|
height: playerHeight, |
|
playerVars: { |
|
'fs': 1, |
|
'hd': 1 |
|
}, |
|
events: { |
|
'onReady': onPlayerReady |
|
}, |
|
enablejsapi: '1' |
|
}); |
|
|
|
function onPlayerReady(event) { |
|
|
|
d3.select('#player').on('mouseleave', stopVideo); |
|
d3.select(window).on('mouseleave', stopVideo); |
|
} |
|
} |
|
|
|
function stopVideo() { |
|
if (player) { |
|
player.stopVideo(); |
|
var p = d3.select('#playerObj'); |
|
p.style('display', 'none'); |
|
d3.event.preventDefault(); |
|
|
|
} |
|
}; |
|
|
|
var json = Q.nfbind(d3.json); |
|
|
|
function doPlayLists(channelId) { |
|
stopVideo(); |
|
d3.select('#articles').selectAll('div').remove(); |
|
d3.select('#playListTitle').text('情報取得中..').style('color', 'blue'); |
|
var nextPageToken = ''; |
|
var fetching = false; |
|
var playListItems = []; |
|
|
|
function getPlayLists(pageToken) { |
|
fetching = true; |
|
var defer = Q.defer(); |
|
var pt = ''; |
|
if (pageToken) { |
|
pt = '&pageToken=' + pageToken; |
|
} |
|
json('https://www.googleapis.com/youtube/v3/playlists?part=snippet&channelId=' + channelId + '&order=date&maxResults=50' + pt + '&key=' + youtube_apikey) |
|
.then(function (d) { |
|
Array.prototype.push.apply(playListItems, d.items); |
|
nextPageToken = d.nextPageToken; |
|
defer.resolve(); |
|
}); |
|
return defer.promise; |
|
} |
|
|
|
d3.select(window) |
|
.on("scroll", maybeFetch) |
|
.on("resize", maybeFetch); |
|
|
|
function maybeFetch() { |
|
if (!fetching && nextPageToken && d3.select("#article").node().getBoundingClientRect().top < window.innerHeight) { |
|
getPlayLists(nextPageToken).then(displayPlayLists); |
|
} |
|
} |
|
|
|
getPlayLists().then(displayPlayLists); |
|
|
|
function displayPlayLists() { |
|
fetching = false; |
|
d3.select('#playListTitle').text(''); |
|
//var videos = result.filter( |
|
// function (e, i, a) { |
|
// return e.id.kind == 'youtube#video'; |
|
// }); |
|
|
|
var thumb = d3.select('#articles').selectAll('div') |
|
.data(playListItems, function (d) { return d.id;}) |
|
.enter() |
|
.append('div') |
|
.classed({ 'col-xs-6': true,'col-sm-3': true, 'col-md-2': true, 'col-lg-2': true }) |
|
.append('div') |
|
.classed('thumbnail', true) |
|
.style('overflow', 'auto') |
|
.style('min-height', '140px'); |
|
|
|
var anchor = thumb.append('a') |
|
.attr('href', '#') |
|
.attr('target', '_blank') |
|
.on('click', function (d) { |
|
d3.event.preventDefault(); |
|
doThumbnailVideos(d); |
|
}); |
|
|
|
anchor.append('img') |
|
.attr('src', function (d) { return d.snippet.thumbnails.high.url; }) |
|
.attr('alt', function (d) { return d.snippet.title; }); |
|
|
|
anchor.append('p') |
|
.text(function (d) { return d.snippet.title; }) |
|
.classed('thumbnail-title', true) |
|
.each(function (d) { |
|
|
|
var o = d3.select(this.parentNode.parentNode); |
|
var rect = o.node().getBoundingClientRect(); |
|
d3.select(this).style('top', '5px') |
|
.style('left', '20px') |
|
.style('width', (rect.width - 10) + 'px') |
|
.style('height', '40px'); |
|
|
|
}); |
|
} |
|
} |
|
|
|
function doThumbnailVideos(d) { |
|
var playlistID = d.id; |
|
d3.select('#articles').selectAll('div').remove(); |
|
d3.select('#playListTitle') |
|
.text(function (dt) { return d.snippet.title; }); |
|
var nextPageToken = ''; |
|
var fetching = false; |
|
var videoItems = []; |
|
|
|
function getData(pageToken) { |
|
var defer = Q.defer(); |
|
fetching = true; |
|
var pt = ''; |
|
if (pageToken) { |
|
pt = '&pageToken=' + pageToken; |
|
} |
|
json('https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=' + playlistID + '&order=date&maxResults=50' + pt + '&key=' + youtube_apikey) |
|
.then(function (d) { |
|
defer.resolve(d); |
|
}); |
|
return defer.promise; |
|
} |
|
|
|
d3.select(window) |
|
.on("scroll", maybeFetch) |
|
.on("resize", maybeFetch); |
|
|
|
function maybeFetch() { |
|
if (!fetching && nextPageToken && d3.select("#article").node().getBoundingClientRect().top < window.innerHeight) { |
|
getData(nextPageToken).then(displayThumbnail); |
|
} |
|
} |
|
|
|
function displayThumbnail(d) { |
|
fetching = false; |
|
nextPageToken = d.nextPageToken; |
|
Array.prototype.push.apply(videoItems, d.items); |
|
var vthumb = d3.select('#articles').selectAll('div') |
|
.data(videoItems, function (d) { return d.id; }) |
|
.enter() |
|
.append('div') |
|
.classed({ 'col-xs-6': true, 'col-sm-3': true, 'col-md-2': true, 'col-lg-2': true }) |
|
.append('div') |
|
.classed('thumbnail', true) |
|
.style('overflow', 'auto') |
|
.style('min-height','140px'); |
|
|
|
vthumb.append('img') |
|
.attr('src', function (d) { return d.snippet.thumbnails.high.url; }) |
|
.attr('alt', function (d) { return d.snippet.title; }); |
|
|
|
vthumb.on('mouseenter', function (d) { |
|
var o = d3.select(this); |
|
var rect = o.node().getBoundingClientRect(); |
|
var p = d3.select('#playerObj'); |
|
player.setSize(rect.width, rect.height); |
|
p.style('width', rect.width + 'px') |
|
.style('height', rect.height + 'px') |
|
.style('left', (rect.left + window.scrollX) + 'px') |
|
.style('top', (rect.top + window.scrollY) + 'px') |
|
.style('display', 'block'); |
|
player.loadVideoById({ videoId: d.snippet.resourceId.videoId }); |
|
d3.event.preventDefault(); |
|
}); |
|
|
|
vthumb.append('p') |
|
.text(function (d) { return d.snippet.title; }) |
|
.classed('thumbnail-title', true) |
|
.each(function (d) { |
|
var o = d3.select(this.parentNode); |
|
var rect = o.node().getBoundingClientRect(); |
|
d3.select(this).style('top', '5px') |
|
.style('left', '20px') |
|
.style('width', (rect.width - 10) + 'px') |
|
.style('height', (rect.height - 10) + 'px'); |
|
}); |
|
|
|
} |
|
// 初期設定 |
|
getData(nextPageToken) |
|
.then(displayThumbnail); |
|
|
|
} |
|
|
|
doPlayLists('UCgwM0kBBsDRMZDhhWuTNR-g'); |
|
</script> |
|
</body> |
|
</html> |