~ubuntu-branches/ubuntu/raring/banshee/raring

« back to all changes in this revision

Viewing changes to src/Extensions/Banshee.UPnPClient/Banshee.UPnPClient/UPnPServerSource.cs

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2012-02-18 22:17:24 UTC
  • mfrom: (6.3.23 experimental)
  • Revision ID: package-import@ubuntu.com-20120218221724-cnwdvn490wjjue8t
Tags: 2.3.5-1ubuntu1
* Merge from Debian Experimental, remaining changes:
  + Enable and recommend SoundMenu and Disable NotificationArea by default
  + Disable boo and karma extensions
  + Move desktop file for Meego UI to /usr/share/une/applications
  + Change the url for the Amazon store redirector

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// UPnPContainerSource.cs
 
3
//
 
4
// Authors:
 
5
//   Tobias 'topfs2' Arrskog <tobias.arrskog@gmail.com>
 
6
//
 
7
// Copyright (C) 2011 Tobias 'topfs2' Arrskog
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining
 
10
// a copy of this software and associated documentation files (the
 
11
// "Software"), to deal in the Software without restriction, including
 
12
// without limitation the rights to use, copy, modify, merge, publish,
 
13
// distribute, sublicense, and/or sell copies of the Software, and to
 
14
// permit persons to whom the Software is furnished to do so, subject to
 
15
// the following conditions:
 
16
//
 
17
// The above copyright notice and this permission notice shall be
 
18
// included in all copies or substantial portions of the Software.
 
19
//
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
27
//
 
28
 
 
29
using System;
 
30
using System.Collections.Generic;
 
31
 
 
32
using Mono.Unix;
 
33
 
 
34
using Mono.Upnp;
 
35
using Mono.Upnp.Dcp.MediaServer1.ContentDirectory1;
 
36
using Mono.Upnp.Dcp.MediaServer1.ContentDirectory1.AV;
 
37
 
 
38
using Banshee.Base;
 
39
using Banshee.Collection;
 
40
using Banshee.Configuration;
 
41
using Banshee.Sources;
 
42
using Banshee.ServiceStack;
 
43
 
 
44
using Hyena;
 
45
 
 
46
namespace Banshee.UPnPClient
 
47
{
 
48
    public class UPnPServerSource : Source
 
49
    {
 
50
        private string udn;
 
51
        private UPnPMusicSource music_source;
 
52
        private UPnPVideoSource video_source;
 
53
        private SchemaEntry<bool> expanded_schema;
 
54
 
 
55
        public UPnPServerSource (Device device) :  base (Catalog.GetString ("UPnP Share"), device.FriendlyName, 300)
 
56
        {
 
57
            Properties.SetStringList ("Icon.Name", "computer", "network-server");
 
58
            TypeUniqueId = "upnp-container";
 
59
            expanded_schema = new SchemaEntry<bool> ("plugins.upnp." + device.Udn, "expanded", true,
 
60
                                                     "UPnP Share expanded", "UPnP Share expanded" );
 
61
            udn = device.Udn;
 
62
 
 
63
            ContentDirectoryController content_directory = null;
 
64
 
 
65
            foreach (Service service in device.Services) {
 
66
                Log.Debug ("UPnPService \"" + device.FriendlyName + "\" Implements " + service.Type);
 
67
                if (service.Type.Type == "ContentDirectory") {
 
68
                    content_directory = new ContentDirectoryController (service.GetController());
 
69
                }
 
70
            }
 
71
 
 
72
            if (content_directory == null) {
 
73
                throw new ArgumentNullException("content_directory");
 
74
            }
 
75
 
 
76
            ThreadAssist.Spawn (delegate {
 
77
                Parse (device, content_directory);
 
78
            });
 
79
            
 
80
        }
 
81
 
 
82
        ~UPnPServerSource ()
 
83
        {
 
84
            if (music_source != null) {
 
85
                RemoveChildSource (music_source);
 
86
                music_source = null;
 
87
            }
 
88
 
 
89
            if (video_source != null) {
 
90
                RemoveChildSource (video_source);
 
91
                video_source = null;
 
92
            }
 
93
        }
 
94
 
 
95
        delegate void ChunkHandler<T> (Results<T> results);
 
96
 
 
97
        void HandleResults<T> (Results<T> results, RemoteContentDirectory remote_dir, ChunkHandler<T> chunkHandler)
 
98
        {
 
99
            bool has_results = results.Count > 0;
 
100
 
 
101
            while (has_results) {
 
102
                chunkHandler (results);
 
103
 
 
104
                has_results = results.HasMoreResults;
 
105
                if (has_results) {
 
106
                    results = results.GetMoreResults (remote_dir);
 
107
                }
 
108
            }
 
109
        }
 
110
 
 
111
        List<string[]> FindBrowseQuirks (Device device)
 
112
        {
 
113
            List<string[]> core = new List<string[]>();
 
114
            if (device.ModelName == "MediaTomb" && device.ModelNumber == "0.12.1") {
 
115
                core.Add (new string[2] { "Audio", "Albums" });
 
116
                core.Add (new string[2] { "Video", "All Video" });
 
117
            } else if (device.ModelName == "Coherence UPnP A/V MediaServer" && device.ModelNumber == "0.6.6.2") {
 
118
                core.Add (new string[1] { "Albums" });
 
119
            } else {
 
120
              core.Add (new string[0]);
 
121
            }
 
122
 
 
123
            return core;
 
124
        }
 
125
 
 
126
        void Parse (Device device, ContentDirectoryController content_directory)
 
127
        {
 
128
            RemoteContentDirectory remote_dir = new RemoteContentDirectory (content_directory);
 
129
            DateTime begin = DateTime.Now;
 
130
            Container root = remote_dir.GetRootObject ();
 
131
            bool recursive_browse = !content_directory.CanSearch;
 
132
 
 
133
            try {
 
134
                if (!recursive_browse) {
 
135
                    try {
 
136
                        Log.Debug ("Content directory is searchable, let's search");
 
137
 
 
138
                        HandleResults<MusicTrack> (remote_dir.Search<MusicTrack>(root, visitor => visitor.VisitDerivedFrom("upnp:class", "object.item.audioItem.musicTrack"), new ResultsSettings()),
 
139
                                                   remote_dir,
 
140
                                                   chunk => {
 
141
                                                                List<MusicTrack> music_tracks = new List<MusicTrack>();
 
142
                                                                foreach (var item in chunk) {
 
143
                                                                    music_tracks.Add (item as MusicTrack);
 
144
                                                                }
 
145
 
 
146
                                                                AddMusic (music_tracks);
 
147
                                                            });
 
148
 
 
149
                        HandleResults<VideoItem>  (remote_dir.Search<VideoItem>(root, visitor => visitor.VisitDerivedFrom("upnp:class", "object.item.videoItem"), new ResultsSettings()),
 
150
                                                   remote_dir,
 
151
                                                   chunk => {
 
152
                                                                List<VideoItem> video_tracks = new List<VideoItem>();
 
153
                                                                foreach (var item in chunk) {
 
154
                                                                    video_tracks.Add (item as VideoItem);
 
155
                                                                }
 
156
 
 
157
                                                                AddVideo (video_tracks);
 
158
                                                            });
 
159
                    } catch (System.InvalidCastException exception) {
 
160
                        Log.Exception (exception);
 
161
                        recursive_browse = true;
 
162
                    }
 
163
                }
 
164
 
 
165
                if (recursive_browse) {
 
166
                    Log.Debug ("Content directory is not searchable, let's browse recursively");
 
167
                    List<MusicTrack> music_tracks = new List<MusicTrack> ();
 
168
                    List<VideoItem> video_tracks = new List<VideoItem> ();
 
169
 
 
170
                    foreach (var hierarchy in FindBrowseQuirks (device)) {
 
171
                        TraverseContainer (remote_dir, root, hierarchy, 0, music_tracks, video_tracks);
 
172
                    }
 
173
 
 
174
                    if (music_tracks.Count > 0) {
 
175
                        AddMusic (music_tracks);
 
176
                    }
 
177
                    if (video_tracks.Count > 0) {
 
178
                        AddVideo (video_tracks);
 
179
                    }
 
180
                }
 
181
            } catch (Exception exception) {
 
182
                Log.Exception (exception);
 
183
            }
 
184
 
 
185
            Log.Debug ("Found all items on the service, took " + (DateTime.Now - begin).ToString());
 
186
        }
 
187
 
 
188
        void TraverseContainer (RemoteContentDirectory remote_dir, Container container, string[] hierarchy, int position, List<MusicTrack> music_tracks, List<VideoItem> video_tracks)
 
189
        {
 
190
            if (hierarchy != null && hierarchy.Length > position) {
 
191
                HandleResults<Mono.Upnp.Dcp.MediaServer1.ContentDirectory1.Object> (
 
192
                    remote_dir.GetChildren<Mono.Upnp.Dcp.MediaServer1.ContentDirectory1.Object> (container),
 
193
                    remote_dir,
 
194
                    chunk => {
 
195
                        foreach (var upnp_object in chunk) {
 
196
                            if (upnp_object is Container && upnp_object.Title == hierarchy[position]) {
 
197
                                TraverseContainer (remote_dir, upnp_object as Container, hierarchy, position + 1, music_tracks, video_tracks);
 
198
                            }
 
199
                        }
 
200
                    });
 
201
            } else {
 
202
                ParseContainer (remote_dir, container, 0, music_tracks, video_tracks);
 
203
            }
 
204
        }
 
205
 
 
206
        void ParseContainer (RemoteContentDirectory remote_dir, Container container, int depth, List<MusicTrack> music_tracks, List<VideoItem> video_tracks)
 
207
        {
 
208
            if (depth > 10 || (container.ChildCount != null && container.ChildCount == 0)) {
 
209
                return;
 
210
            }
 
211
 
 
212
            HandleResults<Mono.Upnp.Dcp.MediaServer1.ContentDirectory1.Object> (
 
213
                remote_dir.GetChildren<Mono.Upnp.Dcp.MediaServer1.ContentDirectory1.Object> (container),
 
214
                remote_dir,
 
215
                chunk => {
 
216
                    foreach (var upnp_object in chunk) {
 
217
                        if (upnp_object is Item) {
 
218
                            Item item = upnp_object as Item;
 
219
 
 
220
                            if (item.IsReference || item.Resources.Count == 0) {
 
221
                                continue;
 
222
                            }
 
223
 
 
224
                            if (item is MusicTrack) {
 
225
                                music_tracks.Add (item as MusicTrack);
 
226
                            } else if (item is VideoItem) {
 
227
                                video_tracks.Add (item as VideoItem);
 
228
                            }
 
229
                        } else if (upnp_object is Container) {
 
230
                            ParseContainer (remote_dir, upnp_object as Container, depth + 1, music_tracks, video_tracks);
 
231
                        }
 
232
 
 
233
                        if (music_tracks.Count > 500) {
 
234
                            AddMusic (music_tracks);
 
235
                            music_tracks.Clear ();
 
236
                        }
 
237
                        if (video_tracks.Count > 100) {
 
238
                            AddVideo (video_tracks);
 
239
                            video_tracks.Clear ();
 
240
                        }
 
241
                    }
 
242
                });
 
243
        }
 
244
 
 
245
        public void Disconnect ()
 
246
        {
 
247
            if (music_source != null) {
 
248
                music_source.Disconnect ();
 
249
            }
 
250
 
 
251
            if (video_source != null) {
 
252
                video_source.Disconnect ();
 
253
            }
 
254
        }
 
255
 
 
256
        private void AddMusic (List<MusicTrack> tracks)
 
257
        {
 
258
            if (music_source == null) {
 
259
                music_source = new UPnPMusicSource (udn);
 
260
                AddChildSource (music_source);
 
261
            }
 
262
 
 
263
            music_source.AddTracks (tracks);
 
264
        }
 
265
 
 
266
        private void AddVideo (List<VideoItem> tracks)
 
267
        {
 
268
            if (video_source == null) {
 
269
                video_source = new UPnPVideoSource (udn);
 
270
                AddChildSource (video_source);
 
271
            }
 
272
 
 
273
            video_source.AddTracks (tracks);
 
274
        }
 
275
 
 
276
        public override bool? AutoExpand {
 
277
            get { return expanded_schema.Get (); }
 
278
        }
 
279
 
 
280
        public override bool Expanded {
 
281
            get { return expanded_schema.Get (); }
 
282
            set { expanded_schema.Set (value); }
 
283
        }
 
284
 
 
285
        public override bool CanActivate {
 
286
            get { return false; }
 
287
        }
 
288
 
 
289
        public override bool CanRename {
 
290
            get { return false; }
 
291
        }
 
292
    }
 
293
}