MLMediaLibrary+Migration.m 7.67 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*****************************************************************************
 * MLMediaLibrary+Migration.m
 * MobileMediaLibraryKit
 *****************************************************************************
 * Copyright (c) 2015 VideoLAN. All rights reserved.
 * $Id$
 *
 * Authors: Tobias Conradi <videolan # tobias-conradi.de>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/


#import "MLMediaLibrary+Migration.h"
#import "MLTitleDecrapifier.h"
#import "MLFile.h"
#import "MLLabel.h"
#import "MLShowEpisode.h"
#import "MLShow.h"
#import "MLThumbnailerQueue.h"
#import "MLAlbumTrack.h"
#import "MLAlbum.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
23 24 25 26 27
#if HAVE_BLOCK
#import "MLMovieInfoGrabber.h"
#import "MLTVShowInfoGrabber.h"
#import "MLTVShowEpisodesInfoGrabber.h"
#endif
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

@implementation MLMediaLibrary (Migration)

- (void)_setupLibraryPathPriorToMigration
{
    NSString *basePath = nil;
    NSString *groupPath = [self _groupURL].path;
    if ([self _migrationToGroupsNeeded] || groupPath == nil) {
        basePath = [self _oldBasePath];
    } else {
        basePath = groupPath;
    }
    self.libraryBasePath = basePath;
}

- (BOOL)_libraryMigrationNeeded
{
    BOOL migrationNeeded = [self _migrationToGroupsNeeded];
    if (!migrationNeeded) {
        NSError *error;
        migrationNeeded = [self _migrationNeeded:&error];
        if (error!=nil) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
50
            APLog(@"Failed to check if model migration is needed %@", error);
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
        }
    }
    return migrationNeeded;
}

- (void)_migrateLibrary
{
    // triggers automatic model migration
    [self persistentStoreCoordinator];

    if (![self _migrationToGroupsNeeded]) {
        return;
    }

    NSError *error;
    NSString *groupPath = [self _groupURL].path;
    if ([self _migrateLibraryToBasePath:groupPath error:&error]) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
68
        APLog(@"Failed to migrate to group path with error: %@",error);
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
    }
}

#pragma mark - model version migrations

- (BOOL)_migrationNeeded:(NSError **) migrationCheckError
{
    BOOL migrationNeeded = NO;

    if ([[NSFileManager defaultManager] fileExistsAtPath:((MLMediaLibrary *)[MLMediaLibrary sharedMediaLibrary]).persistentStoreURL.path]) {
        NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType
                                                                                                  URL:((MLMediaLibrary *)[MLMediaLibrary sharedMediaLibrary]).persistentStoreURL
                                                                                                error:migrationCheckError];
        if (*migrationCheckError) {
            return NO;
        }
        NSManagedObjectModel *destinationModel = [[MLMediaLibrary sharedMediaLibrary] managedObjectModel];
        migrationNeeded = ![destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata];
    }

    return migrationNeeded;
}


#pragma mark - group path migration

- (BOOL)_migrationToGroupsNeeded
{
    /*
     * We can't and don't need to migrate to groups on pre-iOS 7
     */
    if (![[NSFileManager defaultManager] respondsToSelector:@selector(containerURLForSecurityApplicationGroupIdentifier:)]) {
        return NO;
    }

104 105 106 107
    if ([self _groupURL] == nil) {
        return NO;
    }

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
    NSString *oldPersistentStorePath = [[self _oldBasePath] stringByAppendingPathComponent: @"MediaLibrary.sqlite"];
    return [[NSFileManager defaultManager] fileExistsAtPath:oldPersistentStorePath];
}

- (NSString *)_oldBasePath
{
    NSSearchPathDirectory directory = NSLibraryDirectory;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
    return paths.firstObject;
}

- (NSURL *)_groupURL {
    if (![[NSFileManager defaultManager] respondsToSelector:@selector(containerURLForSecurityApplicationGroupIdentifier:)]) {
        return nil;
    }

    NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:self.applicationGroupIdentifier];
#if TARGET_IPHONE_SIMULATOR
    // if something went wrong with the entitlements in the Simulator
    if (!groupURL) {
        NSArray *pathComponents = [[[NSBundle mainBundle] bundlePath] pathComponents];
        pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, pathComponents.count-4)];
        NSString *groupComponent = [@"Shared/AppGroup/fake-" stringByAppendingString:self.applicationGroupIdentifier];
        NSString *groupPath = [[NSString pathWithComponents:pathComponents] stringByAppendingPathComponent:groupComponent];
        groupURL = [NSURL fileURLWithPath:groupPath];
133
        [[NSFileManager defaultManager] createDirectoryAtURL:groupURL withIntermediateDirectories:YES attributes:nil error:nil];
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
    }
#endif
    return groupURL;
}

- (BOOL)_migrateLibraryToBasePath:(NSString *)basePath error:(NSError *__autoreleasing *)migrationError
{
    BOOL success = YES;
    NSPersistentStoreCoordinator *coordinater = [self persistentStoreCoordinator];
    if (!coordinater) {
        APLog(@"no persistent store coordinator found, migration will fail");
        return NO;
    }
    NSURL *oldStoreURL = self.persistentStoreURL;
    NSPersistentStore *oldStore = [coordinater persistentStoreForURL:oldStoreURL];
    NSString *oldThumbnailPath = self.thumbnailFolderPath;

    self.libraryBasePath = basePath;

    NSURL *newURL = self.persistentStoreURL;
    NSError *error = nil;

#ifdef DEBUG
    // when debugging we want to clean the new base path before doing the migration
    NSError *deleteError;
    NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:basePath] includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants | NSDirectoryEnumerationSkipsHiddenFiles errorHandler:nil];
    for (NSString *pathToDelete in enumerator) {
        if (![[NSFileManager defaultManager] removeItemAtPath:pathToDelete error:&deleteError]) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
162
            APLog(@"Failed to clear object from new base path for migration debugging: %@",deleteError);
163 164 165 166 167 168
        }
    }
#endif

    NSURL *directoryURL = [newURL URLByDeletingLastPathComponent];
    if (![[NSFileManager defaultManager] createDirectoryAtURL:directoryURL withIntermediateDirectories:YES attributes:nil error:&error]) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
169
        APLog(@"Failed to created new directories for url: %@",directoryURL);
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
    }
    NSPersistentStore *newStore = [coordinater migratePersistentStore:oldStore
                                                                toURL:newURL
                                                              options:oldStore.options
                                                             withType:oldStore.type
                                                                error:&error];
    if (!newStore) {
        success = NO;
        APLog(@"Failed to migrate library to new path with error: %@",error);
    } else {
        NSString *oldStorePath = [oldStoreURL path];
        // remove all sqlite remainings
        for (NSString *extension in @[@"",@"-wal",@"-shm"]) {
            NSString *path = [oldStorePath stringByAppendingString:extension];
            if ([[NSFileManager defaultManager] fileExistsAtPath:path] && ![[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
185
                APLog(@"Failed to remove old library with error: %@",error);
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
            }
        }
    }

    NSString *newThumbnailPath = self.thumbnailFolderPath;
    if (![[NSFileManager defaultManager] moveItemAtPath:oldThumbnailPath toPath:newThumbnailPath error:&error]) {
        success = NO;
        APLog(@"Failed to move thumbnails to new path with error: %@",error);
    }

    if (migrationError != nil && error) {
        *migrationError = error;
    }
    return success;
}

@end