| 1 | // |
|---|
| 2 | // SapphireFileDataImporter.m |
|---|
| 3 | // Sapphire |
|---|
| 4 | // |
|---|
| 5 | // Created by pnmerrill on 6/24/07. |
|---|
| 6 | // Copyright 2007 __www.nanopi.net__. All rights reserved. |
|---|
| 7 | // |
|---|
| 8 | |
|---|
| 9 | #import "SapphireFileDataImporter.h" |
|---|
| 10 | #import "SapphireMetaData.h" |
|---|
| 11 | #include <sys/types.h> |
|---|
| 12 | #include <sys/stat.h> |
|---|
| 13 | |
|---|
| 14 | //Single Attributes |
|---|
| 15 | #define MEDIA_TVSHOW_XML_QUERY @"/media[@type='TV Show']/text()" |
|---|
| 16 | #define MEDIA_MOVIE_XML_QUERY @"/media[@type='Movie']/text()" |
|---|
| 17 | #define SUMMARY_XML_QUERY @"/media/summary/text()" |
|---|
| 18 | #define DESCRIPTION_XML_QUERY @"/media/description/text()" |
|---|
| 19 | #define PUBLISHER_XML_QUERY @"/media/publisher/text()" |
|---|
| 20 | #define COPYRIGHT_XML_QUERY @"/media/copyright/text()" |
|---|
| 21 | #define USER_RATING_XML_QUERY @"/media/userStarRating/text()" |
|---|
| 22 | #define RATING_XML_QUERY @"/media/rating/text()" |
|---|
| 23 | #define SERIES_NAME_XML_QUERY @"/media/seriesName/text()" |
|---|
| 24 | #define BROADCASTER_XML_QUERY @"/media/broadcaster/text()" |
|---|
| 25 | #define EPISODE_NUMBER_XML_QUERY @"/media/episodeNumber/text()" |
|---|
| 26 | #define EPISODE_XML_QUERY @"/media/episode/text()" |
|---|
| 27 | #define SEASON_XML_QUERY @"/media/season/text()" |
|---|
| 28 | #define PUBLISHED_XML_QUERY @"/media/published/text()" |
|---|
| 29 | //Multi Attributes |
|---|
| 30 | #define TITLE_XML_QUERY @"/media/title/text()" |
|---|
| 31 | #define GENRES_XML_QUERY @"/media/genres/genre/text()" |
|---|
| 32 | #define CAST_XML_QUERY @"/media/cast/name/text()" |
|---|
| 33 | #define DIRECTORS_XML_QUERY @"/media/directors/name/text()" |
|---|
| 34 | #define PRODUCERS_XML_QUERY @"/media/producers/name/text()" |
|---|
| 35 | |
|---|
| 36 | @implementation SapphireFileDataImporter |
|---|
| 37 | |
|---|
| 38 | /*Information to make the XML import easier*/ |
|---|
| 39 | static NSDictionary *xmlSingleAttributes = nil; |
|---|
| 40 | static NSDictionary *xmlMultiAttributes = nil; |
|---|
| 41 | |
|---|
| 42 | +(void) initialize |
|---|
| 43 | { |
|---|
| 44 | xmlSingleAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: |
|---|
| 45 | META_TITLE_KEY, TITLE_XML_QUERY, |
|---|
| 46 | META_SUMMARY_KEY, SUMMARY_XML_QUERY, |
|---|
| 47 | META_DESCRIPTION_KEY, DESCRIPTION_XML_QUERY, |
|---|
| 48 | @"Publisher", PUBLISHER_XML_QUERY, |
|---|
| 49 | META_COPYRIGHT_KEY, COPYRIGHT_XML_QUERY, |
|---|
| 50 | META_SHOW_FAVORITE_RATING_KEY, USER_RATING_XML_QUERY, |
|---|
| 51 | META_SHOW_RATING_KEY, RATING_XML_QUERY, |
|---|
| 52 | META_SHOW_NAME_KEY, SERIES_NAME_XML_QUERY, |
|---|
| 53 | META_SHOW_BROADCASTER_KEY, BROADCASTER_XML_QUERY, |
|---|
| 54 | META_ABSOLUTE_EP_NUMBER_KEY, EPISODE_NUMBER_XML_QUERY, |
|---|
| 55 | META_EPISODE_NUMBER_KEY, EPISODE_XML_QUERY, |
|---|
| 56 | META_SEASON_NUMBER_KEY, SEASON_XML_QUERY, |
|---|
| 57 | META_SHOW_PUBLISHED_DATE_KEY, PUBLISHED_XML_QUERY,nil] ; |
|---|
| 58 | xmlMultiAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: |
|---|
| 59 | @"Genres", GENRES_XML_QUERY, |
|---|
| 60 | @"Cast", CAST_XML_QUERY, |
|---|
| 61 | @"Producers" , PRODUCERS_XML_QUERY, |
|---|
| 62 | @"Directors", DIRECTORS_XML_QUERY,nil]; |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | - (id)init |
|---|
| 66 | { |
|---|
| 67 | self = [super init]; |
|---|
| 68 | if(self == nil) |
|---|
| 69 | return nil; |
|---|
| 70 | |
|---|
| 71 | xmlFileCount=0; |
|---|
| 72 | |
|---|
| 73 | return self; |
|---|
| 74 | } |
|---|
| 75 | |
|---|
| 76 | /*! |
|---|
| 77 | * @brief Sets the importer's data menu |
|---|
| 78 | * |
|---|
| 79 | * @param theDataMenu The importer's menu |
|---|
| 80 | */ |
|---|
| 81 | - (void)setImporterDataMenu:(SapphireImporterDataMenu *)theDataMenu |
|---|
| 82 | { |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | /*! |
|---|
| 86 | * @brief Import a single File |
|---|
| 87 | * |
|---|
| 88 | * @param metaData The file to import |
|---|
| 89 | * @return YES if imported, NO otherwise |
|---|
| 90 | */ |
|---|
| 91 | - (BOOL) importMetaData:(SapphireFileMetaData *)metaData |
|---|
| 92 | { |
|---|
| 93 | /*Initialization*/ |
|---|
| 94 | BOOL ret = NO; |
|---|
| 95 | NSFileManager *fm = [NSFileManager defaultManager]; |
|---|
| 96 | /*Get the file*/ |
|---|
| 97 | /*Check for XML file*/ |
|---|
| 98 | NSString * xmlFilePath=[metaData path] ; |
|---|
| 99 | xmlPathIsDir = NO; |
|---|
| 100 | xmlFilePath=[[xmlFilePath stringByDeletingPathExtension] stringByAppendingPathExtension:@"xml"]; |
|---|
| 101 | if([fm fileExistsAtPath:xmlFilePath isDirectory:&xmlPathIsDir] && !xmlPathIsDir) |
|---|
| 102 | { |
|---|
| 103 | /*Check modification date on XML file*/ |
|---|
| 104 | struct stat sb; |
|---|
| 105 | memset(&sb, 0, sizeof(struct stat)); |
|---|
| 106 | stat([xmlFilePath fileSystemRepresentation], &sb); |
|---|
| 107 | long modTime = sb.st_mtimespec.tv_sec; |
|---|
| 108 | long oldTime = [metaData importedTimeFromSource:META_XML_IMPORT_KEY]; |
|---|
| 109 | if(oldTime != modTime) |
|---|
| 110 | { |
|---|
| 111 | /*Import the XML file and update counts*/ |
|---|
| 112 | [self importXMLFile:xmlFilePath forMeta:metaData] ; |
|---|
| 113 | xmlFileCount++ ; |
|---|
| 114 | ret = YES; |
|---|
| 115 | } |
|---|
| 116 | } |
|---|
| 117 | /*Import file if necessary*/ |
|---|
| 118 | if ([metaData updateMetaData]) |
|---|
| 119 | ret = YES; |
|---|
| 120 | /*Return whether we imported or not*/ |
|---|
| 121 | return ret; |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | /*! |
|---|
| 125 | * @brief The completion text to display |
|---|
| 126 | * |
|---|
| 127 | * @return The completion text to display |
|---|
| 128 | */ |
|---|
| 129 | - (NSString *)completionText |
|---|
| 130 | { |
|---|
| 131 | return BRLocalizedString(@"Sapphire will continue to import new files as it encounters them. You may initiate this import again at any time, and any new or changed files will be imported", @"End text after import of files is complete"); |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | /*! |
|---|
| 135 | * @brief Import and XML file into meta data |
|---|
| 136 | * |
|---|
| 137 | * @param xmlFileName The path of the xml file |
|---|
| 138 | * @param fileMeta The file's meta data |
|---|
| 139 | */ |
|---|
| 140 | - (void)importXMLFile:(NSString *)xmlFileName forMeta: (SapphireFileMetaData *) fileMeta |
|---|
| 141 | { |
|---|
| 142 | /*Read the XML document*/ |
|---|
| 143 | NSURL *url = [NSURL fileURLWithPath:xmlFileName]; |
|---|
| 144 | NSError *error = nil; |
|---|
| 145 | NSMutableDictionary * metaData=[NSMutableDictionary dictionary]; |
|---|
| 146 | NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyXML error:&error]; |
|---|
| 147 | NSXMLElement *root = [document rootElement]; |
|---|
| 148 | if(!root) |
|---|
| 149 | return; |
|---|
| 150 | /* |
|---|
| 151 | NSString *type = [[root attributeForName:@"type"] stringValue]; |
|---|
| 152 | //Need to catch the media type {"TV Show" , "Movie"} |
|---|
| 153 | if(type!=nil)metaData |
|---|
| 154 | */ |
|---|
| 155 | /*Import single attribute items*/ |
|---|
| 156 | NSEnumerator *keyEnum = [xmlSingleAttributes keyEnumerator]; |
|---|
| 157 | NSString *key = nil; |
|---|
| 158 | |
|---|
| 159 | while((key = [keyEnum nextObject]) != nil) |
|---|
| 160 | { |
|---|
| 161 | /*Search for the attribute*/ |
|---|
| 162 | NSArray *objects = [root objectsForXQuery:key error:&error]; |
|---|
| 163 | if([objects count]) |
|---|
| 164 | { |
|---|
| 165 | /*Import the attribute*/ |
|---|
| 166 | [metaData setObject:[[objects objectAtIndex:0] stringValue] forKey:[xmlSingleAttributes objectForKey:key]] ; |
|---|
| 167 | } |
|---|
| 168 | } |
|---|
| 169 | /*Search for multi attribute items*/ |
|---|
| 170 | keyEnum = [xmlMultiAttributes keyEnumerator]; |
|---|
| 171 | while((key = [keyEnum nextObject]) != nil) |
|---|
| 172 | { |
|---|
| 173 | /*Search for the attribute*/ |
|---|
| 174 | NSArray *objects = [root objectsForXQuery:key error:&error]; |
|---|
| 175 | int count = [objects count]; |
|---|
| 176 | NSMutableArray *newData= nil; |
|---|
| 177 | if(!count) |
|---|
| 178 | continue; |
|---|
| 179 | /*Itterate through the attribute's values*/ |
|---|
| 180 | newData = [NSMutableArray arrayWithCapacity:count]; |
|---|
| 181 | NSEnumerator *objectsEnum = [objects objectEnumerator]; |
|---|
| 182 | NSXMLNode *node = nil; |
|---|
| 183 | while((node = [objectsEnum nextObject]) != nil) |
|---|
| 184 | { |
|---|
| 185 | /*Add each value*/ |
|---|
| 186 | [newData addObject:[node stringValue]]; |
|---|
| 187 | } |
|---|
| 188 | /*Import the attribute*/ |
|---|
| 189 | [metaData setObject:newData forKey:[xmlMultiAttributes objectForKey:key]] ; |
|---|
| 190 | } |
|---|
| 191 | /*Special cases*/ |
|---|
| 192 | /*The air date*/ |
|---|
| 193 | NSString *value = [metaData objectForKey:META_SHOW_AIR_DATE]; |
|---|
| 194 | if(value != nil) |
|---|
| 195 | { |
|---|
| 196 | /*Change date string to a number*/ |
|---|
| 197 | [metaData removeObjectForKey:META_SHOW_AIR_DATE]; |
|---|
| 198 | NSDate *newValue = [NSDate dateWithNaturalLanguageString:value]; |
|---|
| 199 | if([newValue timeIntervalSince1970]) |
|---|
| 200 | [metaData setObject:newValue forKey:META_SHOW_AIR_DATE]; |
|---|
| 201 | } |
|---|
| 202 | /*The aquired date*/ |
|---|
| 203 | value = [metaData objectForKey:META_SHOW_AQUIRED_DATE]; |
|---|
| 204 | if(value != nil) |
|---|
| 205 | { |
|---|
| 206 | /*Change date sttring to a number*/ |
|---|
| 207 | [metaData removeObjectForKey:META_SHOW_AQUIRED_DATE]; |
|---|
| 208 | NSDate *newValue = [NSDate dateWithNaturalLanguageString:value]; |
|---|
| 209 | if([newValue timeIntervalSince1970]) |
|---|
| 210 | [metaData setObject:newValue forKey:META_SHOW_AQUIRED_DATE]; |
|---|
| 211 | } |
|---|
| 212 | /*Values which need to be converted to numbers*/ |
|---|
| 213 | NSArray *convertToNumbers = [NSArray arrayWithObjects:META_SHOW_FAVORITE_RATING_KEY, META_ABSOLUTE_EP_NUMBER_KEY, META_SEASON_NUMBER_KEY, META_EPISODE_NUMBER_KEY, nil]; |
|---|
| 214 | NSEnumerator *numEnum = [convertToNumbers objectEnumerator]; |
|---|
| 215 | while((key = [numEnum nextObject]) != nil) |
|---|
| 216 | { |
|---|
| 217 | /*Check for presence of value*/ |
|---|
| 218 | NSString *value = [metaData objectForKey:key]; |
|---|
| 219 | if(value != nil) |
|---|
| 220 | { |
|---|
| 221 | /*Convert to a number, either a double or int, depending on value*/ |
|---|
| 222 | double newValue = [value doubleValue]; |
|---|
| 223 | NSNumber *newNum = [NSNumber numberWithInt:[value intValue]]; |
|---|
| 224 | if(newValue != round(newValue)) |
|---|
| 225 | newNum = [NSNumber numberWithDouble:newValue]; |
|---|
| 226 | [metaData setObject:newNum forKey:key]; |
|---|
| 227 | } |
|---|
| 228 | } |
|---|
| 229 | /*Update modification date*/ |
|---|
| 230 | struct stat sb; |
|---|
| 231 | memset(&sb, 0, sizeof(struct stat)); |
|---|
| 232 | stat([xmlFileName fileSystemRepresentation], &sb); |
|---|
| 233 | long modTime = sb.st_mtimespec.tv_sec; |
|---|
| 234 | /*Import into meta data*/ |
|---|
| 235 | [fileMeta importInfo: metaData fromSource:META_XML_IMPORT_KEY withTime:modTime]; |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | /*! |
|---|
| 239 | * @brief The initial text to display |
|---|
| 240 | * |
|---|
| 241 | * @return The initial text to display |
|---|
| 242 | */ |
|---|
| 243 | - (NSString *)initialText |
|---|
| 244 | { |
|---|
| 245 | return BRLocalizedString(@"Populate File Data", @"Title"); |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | /*! |
|---|
| 249 | * @brief The informative text to display |
|---|
| 250 | * |
|---|
| 251 | * @return The informative text to display |
|---|
| 252 | */ |
|---|
| 253 | - (NSString *)informativeText |
|---|
| 254 | { |
|---|
| 255 | return BRLocalizedString(@"This tool will populate Sapphire's File data. This proceedure may take a while, but you may cancel at any time.", @"Description of the import processes"); |
|---|
| 256 | } |
|---|
| 257 | |
|---|
| 258 | /*! |
|---|
| 259 | * @brief The button title |
|---|
| 260 | * |
|---|
| 261 | * @return The button title |
|---|
| 262 | */ |
|---|
| 263 | - (NSString *)buttonTitle |
|---|
| 264 | { |
|---|
| 265 | return BRLocalizedString(@"Start Populating Data", @"Button"); |
|---|
| 266 | } |
|---|
| 267 | |
|---|
| 268 | /*! |
|---|
| 269 | * @brief The data menu was exhumed |
|---|
| 270 | * |
|---|
| 271 | * @param controller The Controller which was on top |
|---|
| 272 | */ |
|---|
| 273 | - (void) wasExhumedByPoppingController: (BRLayerController *) controller |
|---|
| 274 | { |
|---|
| 275 | } |
|---|
| 276 | @end |
|---|