Ejemplo n.º 1
0
class RarArchive(BaseArchive):

    def __init__(self, file):
        self._archive = RarFile(file)

    def namelist(self):
        return self._archive.namelist()

    def filenames(self):
        return self._archive.namelist()

    def close(self):
        return self._archive.close()

    def is_encrypted(self):
        return self._archive.needs_password()

    def extraxt(self):
        rarfile = rarfile.RarFile.self._archive
        rarfile.extraxt(self._archive._rarfile)

    def extractall(self, file):
        return self._archive.extractall(file)

    def list(self, *args, **kwargs):
        self.printdir(*args, **kwargs)

    def printdir(self, file=None):
        """Print a table of contents for the zip file."""
        print("%-46s %19s %12s" % ("File Name", "Modified    ", "Size"),
              file=file)
        for file_ in self._archive._file_parser.infolist():
            date = "%d-%02d-%02d %02d:%02d:%02d" % file_.date_time[:6]
            print("%-46s %s %12d" % (file_.filename, date, file_.file_size),
                  file=file)
Ejemplo n.º 2
0
def scan_archive(path):
    """Scan an archive from a `path`.

    :param str path: existing path to the archive.
    :return: the scanned video.
    :rtype: :class:`~subliminal.video.Video`

    """
    # check for non-existing path
    if not os.path.exists(path):
        raise ValueError('Path does not exist')

    if not is_rarfile(path):
        raise ValueError("'{0}' is not a valid archive".format(os.path.splitext(path)[1]))

    dir_path, filename = os.path.split(path)

    logger.info('Scanning archive %r in %r', filename, dir_path)

    # Get filename and file size from RAR
    rar = RarFile(path)

    # check that the rar doesnt need a password
    if rar.needs_password():
        raise ValueError('Rar requires a password')

    # raise an exception if the rar file is broken
    # must be called to avoid a potential deadlock with some broken rars
    rar.testrar()

    file_info = [f for f in rar.infolist() if not f.isdir() and f.filename.endswith(VIDEO_EXTENSIONS)]

    # sort by file size descending, the largest video in the archive is the one we want, there may be samples or intros
    file_info.sort(key=operator.attrgetter('file_size'), reverse=True)

    # no video found
    if not file_info:
        raise ValueError('No video in archive')

    # Free the information about irrelevant files before guessing
    file_info = file_info[0]

    # guess
    video_filename = file_info.filename
    video_path = os.path.join(dir_path, video_filename)
    video = Video.fromguess(video_path, guessit(video_path))

    # size
    video.size = file_info.file_size

    return video
Ejemplo n.º 3
0
def unrar(path, rar_files, force, result):  # pylint: disable=too-many-branches,too-many-statements
    """
    Extracts RAR files

    :param path: Path to look for files in
    :param rar_files: Names of RAR files
    :param force: process currently processing items
    :param result: Previous results
    :return: List of unpacked file names
    """

    unpacked_dirs = []

    if sickbeard.UNPACK == 1 and rar_files:
        result.output += log_helper("Packed Releases detected: {0}".format(rar_files), logger.DEBUG)
        for archive in rar_files:
            failure = None
            rar_handle = None
            try:
                archive_path = ek(os.path.join, path, archive)
                if already_processed(path, archive, force, result):
                    result.output += log_helper(
                        "Archive file already post-processed, extraction skipped: {0}".format
                        (archive_path), logger.DEBUG)
                    continue

                if not helpers.is_rar_file(archive_path):
                    continue

                result.output += log_helper("Checking if archive is valid and contains a video: {0}".format(archive_path), logger.DEBUG)
                rar_handle = RarFile(archive_path)
                if rar_handle.needs_password():
                    # TODO: Add support in settings for a list of passwords to try here with rar_handle.set_password(x)
                    result.output += log_helper('Archive needs a password, skipping: {0}'.format(archive_path))
                    continue

                # rar_handle.testrar()

                # If there are no video files in the rar, don't extract it
                rar_media_files = filter(helpers.is_media_file, rar_handle.namelist())
                if not rar_media_files:
                    continue

                rar_release_name = archive.rpartition('.')[0]

                # Choose the directory we'll unpack to:
                if sickbeard.UNPACK_DIR and os.path.isdir(sickbeard.UNPACK_DIR): # verify the unpack dir exists
                    unpack_base_dir = sickbeard.UNPACK_DIR
                else:
                    unpack_base_dir = path
                    if sickbeard.UNPACK_DIR: # Let user know if we can't unpack there
                        result.output += log_helper('Unpack directory cannot be verified. Using {0}'.format(path), logger.DEBUG)

                # Fix up the list for checking if already processed
                rar_media_files = [os.path.join(unpack_base_dir, rar_release_name, rar_media_file) for rar_media_file in rar_media_files]

                skip_rar = False
                for rar_media_file in rar_media_files:
                    check_path, check_file = os.path.split(rar_media_file)
                    if already_processed(check_path, check_file, force, result):
                        result.output += log_helper(
                            "Archive file already post-processed, extraction skipped: {0}".format
                            (rar_media_file), logger.DEBUG)
                        skip_rar = True
                        break

                if skip_rar:
                    continue

                rar_extract_path = ek(os.path.join, unpack_base_dir, rar_release_name)
                result.output += log_helper("Unpacking archive: {0}".format(archive), logger.DEBUG)
                rar_handle.extractall(path=rar_extract_path)
                unpacked_dirs.append(rar_extract_path)

            except RarCRCError:
                failure = ('Archive Broken', 'Unpacking failed because of a CRC error')
            except RarWrongPassword:
                failure = ('Incorrect RAR Password', 'Unpacking failed because of an Incorrect Rar Password')
            except PasswordRequired:
                failure = ('Rar is password protected', 'Unpacking failed because it needs a password')
            except RarOpenError:
                failure = ('Rar Open Error, check the parent folder and destination file permissions.',
                           'Unpacking failed with a File Open Error (file permissions?)')
            except RarExecError:
                failure = ('Invalid Rar Archive Usage', 'Unpacking Failed with Invalid Rar Archive Usage. Is unrar installed and on the system PATH?')
            except BadRarFile:
                failure = ('Invalid Rar Archive', 'Unpacking Failed with an Invalid Rar Archive Error')
            except NeedFirstVolume:
                continue
            except (Exception, Error) as e:
                failure = (ex(e), 'Unpacking failed')
            finally:
                if rar_handle:
                    del rar_handle

            if failure:
                result.output += log_helper('Failed to extract the archive {0}: {1}'.format(archive, failure[0]), logger.WARNING)
                result.missed_files.append('{0} : Unpacking failed: {1}'.format(archive, failure[1]))
                result.result = False
                continue

    return unpacked_dirs
Ejemplo n.º 4
0
    def unrar(self, path, rar_files, force=False):
        """
        Extract RAR files.

        :param path: Path to look for files in
        :param rar_files: Names of RAR files
        :param force: process currently processing items
        :return: List of unpacked file names
        """
        unpacked_files = []

        if app.UNPACK and rar_files:
            self.log_and_output('Packed files detected: {rar_files}',
                                level=logging.DEBUG,
                                **{'rar_files': rar_files})

            for archive in rar_files:
                self.log_and_output('Unpacking archive: {archive}',
                                    level=logging.DEBUG,
                                    **{'archive': archive})

                failure = None
                try:
                    rar_handle = RarFile(os.path.join(path, archive))

                    # check that the rar doesnt need a password
                    if rar_handle.needs_password():
                        raise ValueError('Rar requires a password')

                    # Skip extraction if any file in archive has previously been extracted
                    skip_extraction = False
                    for file_in_archive in [
                            os.path.basename(each.filename)
                            for each in rar_handle.infolist()
                            if not each.isdir()
                    ]:
                        if not force and self.already_postprocessed(
                                file_in_archive):
                            self.log_and_output(
                                'Archive file already post-processed, extraction skipped: {file_in_archive}',
                                level=logging.DEBUG,
                                **{'file_in_archive': file_in_archive})
                            skip_extraction = True
                            break

                        if app.POSTPONE_IF_NO_SUBS and os.path.isfile(
                                os.path.join(path, file_in_archive)):
                            self.log_and_output(
                                'Archive file already extracted, extraction skipped: {file_in_archive}',
                                level=logging.DEBUG,
                                **{'file_in_archive': file_in_archive})
                            skip_extraction = True
                            break

                    if not skip_extraction:
                        # raise an exception if the rar file is broken
                        rar_handle.testrar()
                        rar_handle.extractall(path=path)

                    for each in rar_handle.infolist():
                        if not each.isdir():
                            basename = os.path.basename(each.filename)
                            unpacked_files.append(basename)

                    del rar_handle

                except (BadRarFile, Error, NotRarFile, RarCannotExec,
                        ValueError) as error:
                    failure = (ex(error), 'Unpacking failed with a Rar error')
                except Exception as error:
                    failure = (ex(error),
                               'Unpacking failed for an unknown reason')

                if failure is not None:
                    self.log_and_output(
                        'Failed unpacking archive {archive}: {failure}',
                        level=logging.WARNING,
                        **{
                            'archive': archive,
                            'failure': failure[0]
                        })
                    self.missed_files.append(
                        '{0}: Unpacking failed: {1}'.format(
                            archive, failure[1]))
                    self.result = False
                    continue

            self.log_and_output('Extracted content: {unpacked_files}',
                                level=logging.DEBUG,
                                **{'unpacked_files': unpacked_files})

        return unpacked_files
Ejemplo n.º 5
0
def unrar(path, rar_files, force, result):  # pylint: disable=too-many-branches,too-many-statements
    """
    Extracts RAR files

    :param path: Path to look for files in
    :param rar_files: Names of RAR files
    :param force: process currently processing items
    :param result: Previous results
    :return: List of unpacked file names
    """

    unpacked_dirs = []

    if sickbeard.UNPACK == 1 and rar_files:
        result.output += log_helper("Packed Releases detected: {0}".format(rar_files), logger.DEBUG)
        for archive in rar_files:
            failure = None
            rar_handle = None
            try:
                archive_path = ek(os.path.join, path, archive)
                if already_processed(path, archive, force, result):
                    result.output += log_helper(
                        "Archive file already post-processed, extraction skipped: {0}".format
                        (archive_path), logger.DEBUG)
                    continue

                if not helpers.is_rar_file(archive_path):
                    continue

                result.output += log_helper("Checking if archive is valid and contains a video: {0}".format(archive_path), logger.DEBUG)
                rar_handle = RarFile(archive_path)
                if rar_handle.needs_password():
                    # TODO: Add support in settings for a list of passwords to try here with rar_handle.set_password(x)
                    result.output += log_helper('Archive needs a password, skipping: {0}'.format(archive_path))
                    continue

                # rar_handle.testrar()

                # If there are no video files in the rar, don't extract it
                rar_media_files = filter(helpers.is_media_file, rar_handle.namelist())
                if not rar_media_files:
                    continue

                rar_release_name = archive.rpartition('.')[0]

                # Choose the directory we'll unpack to:
                if sickbeard.UNPACK_DIR and os.path.isdir(sickbeard.UNPACK_DIR): # verify the unpack dir exists
                    unpack_base_dir = sickbeard.UNPACK_DIR
                else:
                    unpack_base_dir = path
                    if sickbeard.UNPACK_DIR: # Let user know if we can't unpack there
                        result.output += log_helper('Unpack directory cannot be verified. Using {0}'.format(path), logger.DEBUG)

                # Fix up the list for checking if already processed
                rar_media_files = [os.path.join(unpack_base_dir, rar_release_name, rar_media_file) for rar_media_file in rar_media_files]

                skip_rar = False
                for rar_media_file in rar_media_files:
                    check_path, check_file = os.path.split(rar_media_file)
                    if already_processed(check_path, check_file, force, result):
                        result.output += log_helper(
                            "Archive file already post-processed, extraction skipped: {0}".format
                            (rar_media_file), logger.DEBUG)
                        skip_rar = True
                        break

                if skip_rar:
                    continue

                rar_extract_path = ek(os.path.join, unpack_base_dir, rar_release_name)
                result.output += log_helper("Unpacking archive: {0}".format(archive), logger.DEBUG)
                rar_handle.extractall(path=rar_extract_path)
                unpacked_dirs.append(rar_extract_path)

            except RarCRCError:
                failure = ('Archive Broken', 'Unpacking failed because of a CRC error')
            except RarWrongPassword:
                failure = ('Incorrect RAR Password', 'Unpacking failed because of an Incorrect Rar Password')
            except PasswordRequired:
                failure = ('Rar is password protected', 'Unpacking failed because it needs a password')
            except RarOpenError:
                failure = ('Rar Open Error, check the parent folder and destination file permissions.',
                           'Unpacking failed with a File Open Error (file permissions?)')
            except RarExecError:
                failure = ('Invalid Rar Archive Usage', 'Unpacking Failed with Invalid Rar Archive Usage. Is unrar installed and on the system PATH?')
            except BadRarFile:
                failure = ('Invalid Rar Archive', 'Unpacking Failed with an Invalid Rar Archive Error')
            except NeedFirstVolume:
                continue
            except (Exception, Error) as e:
                failure = (ex(e), 'Unpacking failed')
            finally:
                if rar_handle:
                    del rar_handle

            if failure:
                result.output += log_helper('Failed to extract the archive {0}: {1}'.format(archive, failure[0]), logger.WARNING)
                result.missed_files.append('{0} : Unpacking failed: {1}'.format(archive, failure[1]))
                result.result = False
                continue

    return unpacked_dirs
Ejemplo n.º 6
0
    def add(self, archive_password=None, audio_file=None, session=None, artist_name_fallback=None):
        cache_key = LibraryUpload.CACHE_KEY % (cherrypy.request.user.id, session)

        all_tracks = None

        if not cache.has(cache_key):
            raise cherrypy.HTTPError(status=409)
        else:
            all_tracks = cache.get(cache_key)

        if audio_file is not None and len(audio_file) == 0:
            audio_file = None

        if archive_password is not None and len(archive_password) == 0:
            archive_password = None

        content_disposition = cherrypy.request.headers.get('content-disposition')

        filename = content_disposition[content_disposition.index('filename=') + 9:]

        if filename.startswith('"') and filename.endswith('"'):
            filename = filename[1:-1]

        filename = unquote(filename)

        ext = os.path.splitext(filename)[1].lower()[1:]
        basename = os.path.splitext(filename)[0]

        cache_path = os.path.join(cherrypy.config['opmuse'].get('cache.path'), 'upload')

        if not os.path.exists(cache_path):
            os.mkdir(cache_path)

        tempdir = tempfile.mkdtemp(dir=cache_path)

        path = os.path.join(tempdir, filename)

        paths = []

        rarfile.PATH_SEP = '/'

        messages = []

        with open(path, 'wb') as fileobj:
            fileobj.write(cherrypy.request.rfile.read())

        # this file is a regular file that belongs to an audio_file
        if audio_file is not None:
            track = None
            tries = 0

            # try and sleep until we get the track.. this will almost always
            # be needed because of the async upload.
            while track is None and tries < 10:
                track = library_dao.get_track_by_filename(audio_file.encode('utf8'))
                tries += 1

                if track is None:
                    time.sleep(3)

            if track is None:
                messages.append(('warning', ("<strong>%s</strong>: Skipping <strong>%s</strong>, timeout trying to " +
                                "find its track.") % (audio_file, filename)))
            else:
                track_structure = TrackStructureParser(track)
                track_path = track_structure.get_path(absolute=True)
                relative_track_path = track_structure.get_path(absolute=False).decode('utf8', 'replace')

                new_path = os.path.join(track_path, filename.encode('utf8'))

                if os.path.exists(new_path):
                    messages.append(('warning', ("<strong>%s</strong>: Skipping <strong>%s</strong>, already exists " +
                                    "in <strong>%s</strong>.") % (audio_file, filename, relative_track_path)))
                else:
                    shutil.move(path.encode('utf8'), new_path)
                    messages.append(('info', ("<strong>%s</strong>: Uploaded <strong>%s</strong> to " +
                                    "<strong>%s</strong>.") % (audio_file, filename, relative_track_path)))

        elif ext == "zip":
            # set artist name fallback to zip's name so if it's missing artist tags
            # it's easily distinguishable and editable so it can be fixed after upload.
            artist_name_fallback = basename

            try:
                zip = ZipFile(path)

                if archive_password is not None:
                    zip.setpassword(archive_password.encode())

                zip.extractall(tempdir)

                os.remove(path)

                for name in zip.namelist():
                    namepath = os.path.join(tempdir, name)

                    # ignore hidden files, e.g. OSX archive weirdness and such
                    if name.startswith(".") or os.path.split(name)[0] == "__MACOSX":
                        shutil.rmtree(namepath)
                        continue

                    paths.append(namepath.encode('utf8'))

            except Exception as error:
                messages.append(('danger', "<strong>%s</strong>: %s" % (os.path.basename(path), error)))

        elif ext == "rar":
            # look at corresponding ext == zip comment...
            artist_name_fallback = basename

            try:
                rar = RarFile(path)

                if archive_password is None and rar.needs_password():
                    messages.append(('danger', "<strong>%s</strong>: Needs password but none provided." %
                                    os.path.basename(path)))
                else:
                    if archive_password is not None:
                        rar.setpassword(archive_password)

                    rar.extractall(tempdir)

                    os.remove(path)

                    for name in rar.namelist():
                        namepath = os.path.join(tempdir, name)

                        if name.startswith("."):
                            shutil.rmtree(namepath)
                            continue

                        paths.append(namepath.encode('utf8'))

            except Exception as error:
                messages.append(('danger', "<strong>%s</strong>: %s" % (os.path.basename(path), error)))

        # this is a plain audio file
        else:
            paths.append(path.encode('utf8'))

        for path in paths:
            # update modified time to now, we don't want the time from the zip
            # archive or whatever
            os.utime(path, None)

        if len(paths) > 0:
            tracks, add_files_messages = library_dao.add_files(paths, move=True, remove_dirs=False,
                                                               artist_name_fallback=artist_name_fallback,
                                                               user=cherrypy.request.user)
            messages += add_files_messages
        else:
            tracks = []

        shutil.rmtree(tempdir)

        for track in tracks:
            all_tracks.append(track.id)

            if track.album is not None:
                remotes.update_album(track.album)

            if track.artist is not None:
                remotes.update_artist(track.artist)

            remotes.update_track(track)

        hierarchy = Library._produce_track_hierarchy(library_dao.get_tracks_by_ids(all_tracks))

        return {'hierarchy': hierarchy, 'messages': messages}