VLCPlexParser.m 12.2 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * VLCPlexParser.m
 * VLC for iOS
 *****************************************************************************
Pierre SAGASPE's avatar
Pierre SAGASPE committed
5
 * Copyright (c) 2014-2015 VideoLAN. All rights reserved.
6
7
8
9
10
11
12
 *
 * Authors: Pierre Sagaspe <pierre.sagaspe # me.com>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/

#import "VLCPlexParser.h"
13
#import "VLCPlexWebAPI.h"
14
#import "SSKeychain.h"
15

16
17
static NSString *const kPlexMediaServerDirInit = @"/library/sections";
static NSString *const kPlexVLCDeviceName = @"VLC for iOS";
18
19
20
21
22
23
24
25
26
27
28

@interface VLCPlexParser () <NSXMLParserDelegate>
{
    NSMutableArray *_containerInfo;
    NSMutableDictionary *_dicoInfo;
    NSString *_PlexMediaServerUrl;
}
@end

@implementation VLCPlexParser

Tobias's avatar
Tobias committed
29
- (NSArray *)PlexMediaServerParser:(NSString *)address port:(NSNumber *)port navigationPath:(NSString *)path authentification:(NSString *)auth error:(NSError *__autoreleasing *)outError
30
31
32
{
    _containerInfo = [[NSMutableArray alloc] init];
    _dicoInfo = [[NSMutableDictionary alloc] init];
33
34
35
36
37
38
39
    NSURLComponents *urlComponents = [[NSURLComponents alloc] init];
    urlComponents.scheme = @"http";
    urlComponents.host = address;
    urlComponents.port = port;

    _PlexMediaServerUrl = [urlComponents URL].absoluteString;

40
41
    NSString *mediaServerUrl;

42
43
44
45
46
47
48
49
    if ([path length] == 0) {
        urlComponents.path = kPlexMediaServerDirInit;
    } else {
        if ([path rangeOfString:@"library"].location != NSNotFound) {
            urlComponents.path = [@"/" stringByAppendingPathComponent:path];
        } else {
            urlComponents.path = [kPlexMediaServerDirInit stringByAppendingPathComponent:path];
        }
50
51
    }

52
    mediaServerUrl = [[urlComponents URL].absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
53

54
    VLCPlexWebAPI *PlexWebAPI = [[VLCPlexWebAPI alloc] init];
55
    NSURL *url = [[NSURL alloc] initWithString:[PlexWebAPI urlAuth:mediaServerUrl authentification:auth]];
56
57
58
59
60
61
62
63
64

    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
    NSHTTPURLResponse *response = nil;
    NSError *error = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

    if ([response statusCode] != 200) {
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        if([responseString rangeOfString:@"Unauthorized"].location != NSNotFound) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
65
            NSString *serviceString = [NSString stringWithFormat:@"plex://%@%@", address, port];
66
            NSArray *accounts = [SSKeychain accountsForService:serviceString];
67
68
69
70
            if (!accounts) {
                serviceString = @"plex://Account";
                accounts = [SSKeychain accountsForService:serviceString];
            }
71
72
73
74
            if (accounts) {
                NSDictionary *account = [accounts firstObject];
                NSString *username = [account objectForKey:@"acct"];
                NSString *password = [SSKeychain passwordForService:serviceString account:username];
75
76

                auth = [PlexWebAPI PlexAuthentification:username password:password];
77
                url = [NSURL URLWithString:[PlexWebAPI urlAuth:mediaServerUrl authentification:auth]];
78
79
80
81
82
                request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
                response = nil;
                error = nil;
                data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
                if ([response statusCode] != 200) {
Tobias's avatar
Tobias committed
83
84
85
86
87
88
89
90
91
92
93
94
95
96
                    if (outError) {

                        NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:
                                                         @{
                                                           NSLocalizedDescriptionKey : NSLocalizedString(@"PLEX_ERROR_ACCOUNT", nil),
                                                           NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"PLEX_CHECK_ACCOUNT", nil),
                                                           }];
                        if (error) {
                            userInfo[NSUnderlyingErrorKey] = error;
                        }
                        *outError = [NSError errorWithDomain:@"org.videolan.vlc-ios.plexParser"
                                                       code:response.statusCode
                                                   userInfo:userInfo];
                    }
97
98
99
100
                }
                [_containerInfo removeAllObjects];
                [_dicoInfo removeAllObjects];
            } else {
Tobias's avatar
Tobias committed
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

                if (outError) {

                    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:
                                                     @{
                                                       NSLocalizedDescriptionKey : NSLocalizedString(@"UNAUTHORIZED", nil),
                                                       NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"PLEX_CHECK_ACCOUNT", nil),
                                                       }];
                    if (error) {
                        userInfo[NSUnderlyingErrorKey] = error;
                    }
                    *outError = [NSError errorWithDomain:@"org.videolan.vlc-ios.plexParser"
                                                    code:response.statusCode
                                                userInfo:userInfo];
                }
116
117
118
119
120
            }
        } else
            APLog(@"PlexParser url Errors : %ld", (long)[response statusCode]);
    }

121
122
123
    if (error) {
        APLog(@"PlexParser url Error: %@", error);
    }
124
    NSXMLParser *xmlparser = [[NSXMLParser alloc] initWithData:data];
125
126
127
128
129
    [xmlparser setDelegate:self];

    if (![xmlparser parse])
        APLog(@"PlexParser url Errors : %@", url);

130
131
    [_containerInfo setValue:auth forKey:@"authentification"];

132
    return [NSArray arrayWithArray:_containerInfo];
133
134
}

135
- (NSArray *)PlexExtractDeviceInfo:(NSData *)data
136
137
138
139
140
141
142
{
    _containerInfo = [[NSMutableArray alloc] init];
    _dicoInfo = [[NSMutableDictionary alloc] init];
    NSXMLParser *xmlparser = [[NSXMLParser alloc] initWithData:data];
    [xmlparser setDelegate:self];

    if (![xmlparser parse])
Felix Paul Kühne's avatar
Felix Paul Kühne committed
143
        APLog(@"PlexParser data Errors : %@", data);
144

145
    return [NSArray arrayWithArray:_containerInfo];
146
147
148
149
}

#pragma mark - Parser

150
151
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
Pierre SAGASPE's avatar
Pierre SAGASPE committed
152
    if ([elementName isEqualToString:@"MediaContainer"]) {
153
154
155
156
        if ([attributeDict objectForKey:@"friendlyName"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"friendlyName"] forKey:@"libTitle"];
        else if ([attributeDict objectForKey:@"title1"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"title1"] forKey:@"libTitle"];
157
158
        if ([attributeDict objectForKey:@"title2"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"title2"] forKey:@"libTitle"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
159
160
        if ([attributeDict objectForKey:@"grandparentTitle"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"grandparentTitle"] forKey:@"grandparentTitle"];
161

Pierre SAGASPE's avatar
Pierre SAGASPE committed
162
    } else if ([elementName isEqualToString:@"Directory"]) {
163
164
165
        [_dicoInfo setObject:@"directory" forKey:@"container"];
        [_dicoInfo setObject:[attributeDict objectForKey:@"key"] forKey:@"key"];
        [_dicoInfo setObject:[attributeDict objectForKey:@"title"] forKey:@"title"];
166

Pierre SAGASPE's avatar
Pierre SAGASPE committed
167
    } else if ([elementName isEqualToString:@"Video"] || [elementName isEqualToString:@"Track"]) {
168
169
170
        [_dicoInfo setObject:@"item" forKey:@"container"];
        [_dicoInfo setObject:[attributeDict objectForKey:@"key"] forKey:@"key"];
        [_dicoInfo setObject:[attributeDict objectForKey:@"title"] forKey:@"title"];
171
        [_dicoInfo setObject:[attributeDict objectForKey:@"ratingKey"] forKey:@"ratingKey"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
172
173
        if ([attributeDict objectForKey:@"summary"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"summary"] forKey:@"summary"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
174
        if ([attributeDict objectForKey:@"viewCount"])
175
176
177
178
            [_dicoInfo setObject:@"watched" forKey:@"state"];
        else
            [_dicoInfo setObject:@"unwatched" forKey:@"state"];

Pierre SAGASPE's avatar
Pierre SAGASPE committed
179
180
    } else if ([elementName isEqualToString:@"Media"]) {
        if ([attributeDict objectForKey:@"audioCodec"])
181
            [_dicoInfo setObject:[attributeDict objectForKey:@"audioCodec"] forKey:@"audioCodec"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
182
        if ([attributeDict objectForKey:@"videoCodec"])
183
            [_dicoInfo setObject:[attributeDict objectForKey:@"videoCodec"] forKey:@"videoCodec"];
184

Pierre SAGASPE's avatar
Pierre SAGASPE committed
185
    } else if ([elementName isEqualToString:@"Part"]) {
186
187
188
        [_dicoInfo setObject:[NSString stringWithFormat:@"%@%@",_PlexMediaServerUrl, [attributeDict objectForKey:@"key"]] forKey:@"keyMedia"];
        if([attributeDict objectForKey:@"file"])
            [_dicoInfo setObject:[[attributeDict objectForKey:@"file"] lastPathComponent] forKey:@"namefile"];
189
        NSString *duration = [[VLCTime timeWithNumber:[attributeDict objectForKey:@"duration"]] stringValue];
190
191
        [_dicoInfo setObject:duration forKey:@"duration"];
        NSString *sizeFile = (NSString *)[attributeDict objectForKey:@"size"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
192
193
194
        if (sizeFile)
            [_dicoInfo setObject:sizeFile forKey:@"size"];

Pierre SAGASPE's avatar
Pierre SAGASPE committed
195
196
    } else if ([elementName isEqualToString:@"Stream"]) {
        if ([attributeDict objectForKey:@"key"])
197
            [_dicoInfo setObject:[NSString stringWithFormat:@"%@%@",_PlexMediaServerUrl, [attributeDict objectForKey:@"key"]] forKey:@"keySubtitle"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
198
        if ([attributeDict objectForKey:@"codec"])
199
            [_dicoInfo setObject:[attributeDict objectForKey:@"codec"] forKey:@"codecSubtitle"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
200
        if ([attributeDict objectForKey:@"language"])
201
            [_dicoInfo setObject:[attributeDict objectForKey:@"language"] forKey:@"languageSubtitle"];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
202
203
        if ([attributeDict objectForKey:@"languageCode"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"languageCode"] forKey:@"languageCode"];
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

    } else if ([elementName isEqualToString:@"Device"] && [[attributeDict objectForKey:@"name"] isEqualToString:kPlexVLCDeviceName]) {
        if ([attributeDict objectForKey:@"name"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"name"] forKey:@"name"];
        if ([attributeDict objectForKey:@"product"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"product"] forKey:@"product"];
        if ([attributeDict objectForKey:@"productVersion"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"productVersion"] forKey:@"productVersion"];
        if ([attributeDict objectForKey:@"platformVersion"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"platformVersion"] forKey:@"platformVersion"];
        if ([attributeDict objectForKey:@"token"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"token"] forKey:@"token"];
        if ([attributeDict objectForKey:@"clientIdentifier"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"clientIdentifier"] forKey:@"clientIdentifier"];
        if ([attributeDict objectForKey:@"id"])
            [_dicoInfo setObject:[attributeDict objectForKey:@"id"] forKey:@"id"];
220
221
    }

222
    if ([attributeDict objectForKey:@"thumb"] && ([elementName isEqualToString:@"Video"] || [elementName isEqualToString:@"Directory"] || [elementName isEqualToString:@"Part"] || [elementName isEqualToString:@"Track"]))
223
224
225
226
227
228
        [_dicoInfo setObject:[NSString stringWithFormat:@"%@%@", _PlexMediaServerUrl, [attributeDict objectForKey:@"thumb"]] forKey:@"thumb"];

}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
229
    if (([elementName isEqualToString:@"Video"] || [elementName isEqualToString:@"Track"] || [elementName isEqualToString:@"Directory"] || [elementName isEqualToString:@"MediaContainer"] || [elementName isEqualToString:@"Device"]) && [_dicoInfo count] > 0) {
230
231
232
233
234
235
        [_containerInfo addObject:_dicoInfo];
        _dicoInfo = [[NSMutableDictionary alloc] init];
    }
}

@end