Skip to content

Commit

Permalink
Web UI: Various improvements to the interface (pull request 17)
Browse files Browse the repository at this point in the history
* Added thumbnails and descriptions for podcasts and episodes
* Changed Ajax calls to jQuery
* Added support for video files
* Stubbed out some methods to track played and position
* Copied images from QML UI (audio.png and video.png)
  • Loading branch information
jmondragon authored and thp committed Oct 1, 2012
1 parent 8aa3bf7 commit d65232c
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 64 deletions.
153 changes: 95 additions & 58 deletions share/gpodder/ui/web/gpodder.js
@@ -1,4 +1,3 @@

gPodder = {
/* XXX: Partially ugly. Some parts would be shorter with JQuery. */

Expand All @@ -24,100 +23,138 @@ gPodder = {
createEpisode: function(episode) {
var li = document.createElement('li');
var a = document.createElement('a');
var img = document.createElement('img');
var wrapper = document.createElement('p');

wrapper.setAttribute('class', 'listwrapper');

if(episode.mime_type.indexOf('video') == 0)
img.setAttribute('src', '/static/images/video.png');
else if(episode.mime_type.indexOf('audio') == 0)
img.setAttribute('src', '/static/images/audio.png');
wrapper.appendChild(img);
//img.setAttribute('class', 'ui-li-icon');
var title = document.createElement('h3');
title.appendChild(document.createTextNode(episode.title));
var description = document.createElement('p');
description.appendChild(document.createTextNode(episode.description));

a.setAttribute('gpodder:episode', JSON.stringify(episode));
a.setAttribute('data-rel', 'dialog');
a.setAttribute('data-transition', 'pop');

a.href = '#episode_details';
a.onclick = function() {
gPodder.selectEpisode(this);
gPodder.currentlySelectedEpisode = this;
};
a.appendChild(wrapper);
a.appendChild(title);
a.appendChild(description);

li.appendChild(a);
a.appendChild(document.createTextNode(episode.title));
return li;
},

createPodcast: function(podcast) {
var li = document.createElement('li');
var img = document.createElement('img');
img.setAttribute('src', podcast.cover_url);
var title = document.createElement('h3');
title.appendChild(document.createTextNode(podcast.title));
var description = document.createElement('p');
description.appendChild(document.createTextNode(podcast.description));

var a = document.createElement('a');
a.setAttribute('gpodder:podcast', JSON.stringify(podcast));
a.href = '#episodes_page';
a.onclick = function() {
gPodder.selectPodcast(this);
gPodder.currentlySelectedPodcast = this;
};
a.appendChild(img);
a.appendChild(title);
a.appendChild(description);

li.appendChild(a);
a.appendChild(document.createTextNode(podcast.title));
return li;
},

init: function() {
var xmlhttp = new XMLHttpRequest();
var podcasts_ul = document.getElementById('podcasts');
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
podcasts_ul.innerHTML = '';

var podcasts = JSON.parse(xmlhttp.responseText);
var lis = podcasts.map(gPodder.createPodcast);
for (i=0; i<lis.length; i++) {
podcasts_ul.appendChild(lis[i]);
}
$('#podcasts').listview('refresh');
}
};
xmlhttp.open('GET', gPodder.url.podcasts, true);
xmlhttp.send(null);
$.getJSON(gPodder.url.podcasts, function(data) {
$('#podcasts').html('');
$.each(data, function() {
$('#podcasts').append(gPodder.createPodcast(this));
})
$('#podcasts').listview('refresh');
});
},

selectPodcast: function(li) {
var podcast = JSON.parse(li.getAttribute('gpodder:podcast'));

var xmlhttp = new XMLHttpRequest();
var episodes_ul = document.getElementById('episodes');
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
episodes_ul.innerHTML = '';
$.getJSON(gPodder.url.episodes(podcast), function(data) {
$('#episodes_title').html(podcast.title);

var episodes = JSON.parse(xmlhttp.responseText);
var lis = episodes.map(gPodder.createEpisode);
for (i=0; i<lis.length; i++) {
episodes_ul.appendChild(lis[i]);
}
$('#episodes').listview('refresh');
}
};
xmlhttp.open('GET', gPodder.url.episodes(podcast), true);
xmlhttp.send(null);
$('#episodes').html('');
$.each(data, function() {
$('#episodes').append(gPodder.createEpisode(this));
})
$('#episodes').listview('refresh');
});
},

selectEpisode: function(li) {
var episode = JSON.parse(li.getAttribute('gpodder:episode'));
var content = '';
var media_url = '';

var keys = new Array();
for (var key in episode) {
keys.push(key);
}
keys.sort();

for (var i=0; i<keys.length; i++) {
var data = episode[keys[i]];

if (keys[i] === 'url') {
content += '<audio controls="controls"><source src="' + data + '" type="audio/mp3" /></audio><br>';
}
if(episode.mime_type.indexOf("audio") == 0)
content += '<div align="center"><audio id="episode_player" width="100%" controls="controls"><source src="' + episode.url + '" type="' + episode.mime_type + '" /></audio></div><br/>';
else if(episode.mime_type.indexOf("video") == 0)
content += '<video width="100%" id="episode_player" controls="controls"><source src="' + episode.url + '" type="' + episode.mime_type + '" /></video><br/>';

// TODO: Add more fields in the dialog box (Pubdate, etc.)
var published = new Date(episode.published * 1000)
content += '<strong>' + episode.title + '</strong>';

//content += '<div data-role="collapsible">';
//content += '<h3>Details</h3>';

content += '<p>' + episode.description + '</p>';
content += '<strong>Released: </strong>' + published.toDateString() + '<br/>';
content += '<strong>Time: </strong>' + published.toTimeString() + '<br/>';
content += '<strong>Size: </strong>' + gPodder.bytesToSize(episode.file_size) + '<br/>';
content += '<strong>Link: </strong><a href="' + episode.link + '" rel="external">' + episode.link + '</a><br/>';
//content += '</div>'; // End collapsible
$('#episode_details_title').text(episode.title);
$('#episode_details_content').html(content);

if (data !== null && data.length !== undefined && data.length > 50) {
data = data.substring(0, 48) + '...';
}
var key = gPodder.quote(keys[i]);
var data = gPodder.quote(data);
content += '<strong>' + key + '</strong> = ' + data + '<br>';
}
// Pause playback if the dialog is closed
$('#episode_details').bind('pagebeforehide', function() {
$('#episode_player').get(0).pause();
});
// Start the player at the last saved position
$('#episode_player').bind('loadedmetadata', function() {
this.currentTime = episode.current_position;
});

// TODO: Mark the episode as played
$('#episode_player').bind('play', function() {
console.log("played episode: " + episode.url);
$.post('/podcast/save', function() {
console.log("POSTED");
})
});

// TODO: Mark the current position
$('#episode_player').bind('pause', function() {
console.log("paused episode: " + episode.url + " at " + this.currentTime);
});

$('#episode_details_content').html(content);
},

bytesToSize: function(bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return 'n/a';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}
};

Binary file added share/gpodder/ui/web/images/audio.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added share/gpodder/ui/web/images/video.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 3 additions & 6 deletions share/gpodder/ui/web/index.html
Expand Up @@ -8,10 +8,9 @@
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>

</head>
<body onload="gPodder.init()">
<div data-role="page" id="podcasts_page">
<div data-role="page" data-theme="a" id="podcasts_page">
<div data-role="header">
<h1>Podcasts</h1>
</div>
Expand All @@ -20,20 +19,18 @@ <h1>Podcasts</h1>
</div>
</div>

<div data-role="page" id="episodes_page">
<div data-role="page" data-theme="a" id="episodes_page" data-add-back-btn="true">
<div data-role="header">
<h1 id="episodes_title">Episodes</h1>
<a href="#podcasts_page" data-icon="back" data-iconpos="notext" data-direction="reverse">Home</a>
</div>
<div data-role="content">
<ul id="episodes" data-role="listview" data-inset="true" data-filter="true"></ul>
</div>
</div>

<div data-role="page" id="episode_details">
<div data-role="page" data-theme="a" id="episode_details" data-transition="pop" data-add-back-btn="true">
<div data-role="header">
<h1 id="episode_details_title">Episode</h1>
<a href="#episodes_page" data-icon="back" data-iconpos="notext" data-direction="reverse">Episodes</a>
</div>
<div data-role="content">
<div id="episode_details_content"></div>
Expand Down
8 changes: 8 additions & 0 deletions share/gpodder/ui/web/style.css
Expand Up @@ -57,3 +57,11 @@ li:hover {
color: black;
}

.listwrapper {
float: left;
width: 50px; height: 50px;
text-align: center;
vertical-align: middle;
margin: 10px;
}

0 comments on commit d65232c

Please sign in to comment.