Skip to content
This repository was archived by the owner on Jul 3, 2020. It is now read-only.

Commit 7aa1ba8

Browse files
author
matt
committed
Get and display album art (also made album listing items larger)
Basic navigation path implementation (not complete yet)
1 parent bd6dc0d commit 7aa1ba8

File tree

4 files changed

+117
-35
lines changed

4 files changed

+117
-35
lines changed

html/css/crdlna.css

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ div.devices {
2727
width : 180px;
2828
}
2929

30+
/** Header/path listing */
31+
#header div {
32+
display : inline-block;
33+
}
34+
3035
/** The right/main pane that contains a a header, main content area and footer */
3136
div.rightStack {
3237
align-items : stretch;
@@ -61,8 +66,8 @@ div.contentItem {
6166
border : 1px solid silver;
6267
border-radius : 3px;
6368
box-shadow : 0 0 0.6em #ccc;
64-
width : 64px;
65-
height : 64px;
69+
width : 96px;
70+
height : 96px;
6671
margin : 8px;
6772
padding : 4px;
6873
cursor : pointer;

html/window.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
</div>
1818

1919
<div class="rightStack">
20-
<div>
21-
Header
20+
<div id="header">
21+
2222
</div>
2323
<div class="contentItemsContainer">
2424
<div id="content" class="contentItems">
2525

26-
content
26+
2727
</div>
2828
</div>
2929

scripts/upnp/MediaServerClient.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,25 +146,34 @@ MediaServerClient.prototype.parseDidl = function (didlDoc) {
146146
var containers = didlDoc.querySelectorAll('container');
147147
var files = didlDoc.querySelectorAll('item');
148148

149+
// Parse containers
149150
[].forEach.call(containers, (function (container, i, a){
150151
var title = container.querySelector('title').textContent;
151152
var type = container.querySelector('class').textContent;
152153
var id = container.getAttribute('id');
153154
var parentId = container.getAttribute('parentID');
154155
var childCount = container.getAttribute('childCount');
156+
157+
// Potentially empty music related bits
158+
var albumArt = container.querySelector('albumArtURI');
159+
var genre = container.querySelector('genre');
160+
var artist = container.querySelector('artist');
155161

156162
if (type && type.indexOf('object.container') >= 0) {
157163
items.push({
158164
title: title,
159165
id: id,
160166
parentId: parentId,
161167
childCount: childCount,
162-
type: type
168+
type: type,
169+
albumArt: (albumArt ? albumArt.textContent : ''),
170+
genre: (genre ? genre.textContent : ''),
171+
artist: (artist ? artist.textContent : '')
163172
});
164173
}
165174
}));
166175

167-
176+
// Parse files
168177
[].forEach.call(files, (function (item, i, a){
169178
var title = item.querySelector('title').textContent;
170179
var type = item.querySelector('class').textContent;

scripts/window/main.js

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
*/
55
var CrDlna = function() {
66
this.clients = {};
7+
8+
// Navigation path stack
9+
this.navigationPath = [];
10+
11+
// The device we are currently looking at
12+
this.currentDevice = undefined;
713
};
814

915
/**
@@ -36,14 +42,13 @@ CrDlna.prototype.updateDeviceList = function () {
3642
}
3743
var container = document.createElement('div');
3844
var device = document.createElement('div');
39-
var listing = document.querySelector('#content')
4045
device.innerText = v.name;
4146
device.setAttribute('objectId', '0');
4247

4348
// If this client has a content directory service, add a click listener
4449
if (that.clients[v.name] && that.clients[v.name].contentDirectory) {
4550
device.addEventListener('click', function (){
46-
51+
that.currentDevice = v;
4752
var sendBrowseRequest = new Promise(function(resolve, reject) {
4853
var client = that.clients[v.name];
4954
if (client) {
@@ -55,7 +60,7 @@ CrDlna.prototype.updateDeviceList = function () {
5560

5661
sendBrowseRequest.then(
5762
function (data) {
58-
that.drawChildFolders(that.clients[v.name].device, listing, data);
63+
that.drawChildFolders(data);
5964
}, function (error) {
6065
console.log("error getting child folders");
6166
});
@@ -70,18 +75,54 @@ CrDlna.prototype.updateDeviceList = function () {
7075

7176
};
7277

78+
/**
79+
* Draws the navigation path
80+
*/
81+
CrDlna.prototype.drawPath = function() {
82+
var that = this;
83+
84+
var heading = document.querySelector('#header');
85+
heading.innerHTML = '';
86+
87+
var item = document.createElement('div');
88+
item.innerHTML = "Device root";
89+
heading.appendChild(item);
90+
91+
this.navigationPath.forEach(function (folder, i, a){
92+
93+
item = document.createElement('div');
94+
item.innerHTML = " &gt; ";
95+
heading.appendChild(item);
96+
97+
item = document.createElement('div');
98+
item.innerHTML = folder.title;
99+
item.addEventListener('click', function() {
100+
101+
while (folder !== that.navigationPath.pop()) {
102+
// keep popping off anything else in the list
103+
}
104+
that.browseFolder(folder);
105+
});
106+
heading.appendChild(item);
107+
108+
});
109+
110+
111+
};
112+
73113
/**
74114
* Draws any child items
75115
*
76-
* @param {Object} elem The element into which to add the child items
77116
* @param {Object} data The object contianing the child folder data
78117
*/
79-
CrDlna.prototype.drawChildFolders = function (device, elem, data) {
80-
if (!data || !elem) return;
118+
CrDlna.prototype.drawChildFolders = function (data) {
81119

82120
var that = this;
121+
var elem = document.querySelector('#content');
83122
elem.innerHTML = '';
84123

124+
if (!data || !elem) return;
125+
85126
var playback = document.querySelector('#playback');
86127

87128
data.forEach(function (v, i, a){
@@ -91,15 +132,16 @@ CrDlna.prototype.drawChildFolders = function (device, elem, data) {
91132
child.setAttribute('type', v.type);
92133
if (v.type.indexOf('videoItem') >= 0) {
93134
// Video Item
94-
child.innerHTML = '<p>' + v.title + '</p><video width="300" height="240" controls preload="none"><source src="' + v.url + '"></video>';
95-
135+
child.innerHTML = '<p>' + v.title + '</p>';
136+
child.addEventListener('click', function() {
137+
playback.innerHTML = '<video width="300" height="240" controls preload="none"><source src="' + v.url + '"></video>';
138+
});
96139
} else if (v.type.indexOf('imageItem') >= 0) {
97140
// Image Item
98141
child.innerHTML = '<p>' + v.title + '</p>';
99142
child.addEventListener('click', function() {
100143
playback.innerHTML = '<webview src="' + v.url + '" width="640" height="480" autosize="on"></webview>';
101144
});
102-
103145
} else if (v.type.indexOf('object.item') >= 0) {
104146
// Audio Item
105147
child.innerHTML = '<p>' + v.title + '</p>';
@@ -109,32 +151,57 @@ CrDlna.prototype.drawChildFolders = function (device, elem, data) {
109151
} else {
110152
// Container
111153
child.classList.add('contentFolder');
112-
child.innerText = v.title;
113-
child.addEventListener('click', function () {
114-
115-
var sendBrowseRequest = new Promise(function(resolve, reject) {
116-
var client = that.clients[device.name];
117-
if (client) {
118-
client.browseFolder(v.id, resolve);
119-
} else {
120-
reject(new Error('No client was found for the device ' + v.name));
121-
}
122-
});
123154

124-
sendBrowseRequest.then(
125-
function (data) {
126-
that.drawChildFolders(device, elem, data);
127-
}, function (error) {
128-
console.log("error getting child folders");
129-
});
155+
// Fairly inefficient albumArt retrieval - have to do it this way due to security restrictions
156+
// of chrome apps. Could be improved by caching to file system
157+
if (v.albumArt !== '') {
158+
var xhr = new XMLHttpRequest();
159+
xhr.open('GET', v.albumArt, true);
160+
xhr.responseType = 'blob';
161+
xhr.onload = function(e) {
162+
child.style.backgroundImage = 'url("' + window.URL.createObjectURL(this.response) + '")';
163+
child.style.backgroundSize = 'cover';
164+
};
165+
xhr.send();
166+
} else {
167+
// load local backup placeholder when we have one
168+
}
130169

170+
child.innerText = v.title;
171+
child.title = v.title;
172+
child.addEventListener('click', function () {
173+
that.browseFolder(v);
131174
});
132175
}
133176
elem.appendChild(child);
134177
});
135178

136179
};
137180

181+
/**
182+
* Browse the folder of this device
183+
*/
184+
CrDlna.prototype.browseFolder = function(folder) {
185+
var that = this;
186+
var sendBrowseRequest = new Promise(function(resolve, reject) {
187+
var client = that.clients[that.currentDevice.name];
188+
if (client) {
189+
that.navigationPath.push(folder);
190+
client.browseFolder(folder.id, resolve);
191+
} else {
192+
reject(new Error('No client was found for the device ' + v.name));
193+
}
194+
});
195+
196+
sendBrowseRequest.then(
197+
function (data) {
198+
that.drawPath();
199+
that.drawChildFolders(data);
200+
}, function (error) {
201+
console.log("error getting child folders");
202+
});
203+
};
204+
138205
/**
139206
* Binds the various UI controls to any handlers that are needed
140207
*/
@@ -151,6 +218,9 @@ CrDlna.prototype.bindControls = function() {
151218

152219
};
153220

221+
/**
222+
* Kicks off everything by creating a new CrDlna object as well as SSDP and UPNP handlers.
223+
*/
154224
document.addEventListener("DOMContentLoaded", function() {
155225
console.log("Starting Chrome DLNA...");
156226
var c = new CrDlna();
@@ -166,8 +236,6 @@ document.addEventListener("DOMContentLoaded", function() {
166236
c.ssdp.init();
167237
c.upnp.init();
168238

169-
170-
171239
// Make sure the SSDP cache resets periodically (doing it here as we have
172240
// access to window.setInterval)
173241
window.setInterval(function(){

0 commit comments

Comments
 (0)