diff --git a/fimfarchive/fetchers.py b/fimfarchive/fetchers.py
deleted file mode 100644
index ab6ef8d..0000000
--- a/fimfarchive/fetchers.py
+++ /dev/null
@@ -1,344 +0,0 @@
-"""
-Fetchers for Fimfarchive.
-"""
-
-
-#
-# Fimfarchive, preserves stories from Fimfiction.
-# Copyright (C) 2015 Joakim Soderlund
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-
-
-import codecs
-import gc
-import json
-from copy import deepcopy
-from io import BytesIO
-from typing import FrozenSet
-from zipfile import ZipFile, BadZipFile
-
-import requests
-
-from fimfarchive.exceptions import InvalidStoryError, StorySourceError
-from fimfarchive.flavors import Flavor, StorySource, DataFormat, MetaPurity
-from fimfarchive.stories import Story
-
-
-__all__ = (
- 'Fetcher',
- 'FimfictionFetcher',
- 'FimfarchiveFetcher',
-)
-
-
-StreamReader = codecs.getreader('utf-8')
-
-
-class Fetcher:
- """
- Abstract base class for story fetchers.
- """
- prefetch_meta = False
- prefetch_data = False
-
- flavors: FrozenSet[Flavor] = frozenset()
-
- def __enter__(self):
- """
- Returns self for use in with statements.
- """
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- """
- Closes the fetcher on exit in with statements.
- """
- self.close()
-
- def close(self):
- """
- Closes file descriptors and frees memory.
- """
- pass
-
- def fetch(self, key, prefetch_meta=None, prefetch_data=None):
- """
- Fetches story information.
-
- Args:
- key: Primary key of the story.
- prefetch_meta: Force prefetching of meta.
- prefetch_data: Force prefetching of data.
-
- Returns:
- Story: A new `Story` object.
-
- Raises:
- InvalidStoryError: If a valid story is not found.
- StorySourceError: If source does not return any data.
- """
- if prefetch_meta is None:
- prefetch_meta = self.prefetch_meta
-
- if prefetch_meta:
- meta = self.fetch_meta(key)
- else:
- meta = None
-
- if prefetch_data is None:
- prefetch_data = self.prefetch_data
-
- if prefetch_data:
- data = self.fetch_data(key)
- else:
- data = None
-
- return Story(key, self, meta, data, self.flavors)
-
- def fetch_data(self, key):
- """
- Fetches story content data.
-
- Args:
- key: Primary key of the story.
-
- Returns:
- bytes: Story content data.
-
- Raises:
- InvalidStoryError: If a valid story is not found.
- StorySourceError: If source does not return any data.
- """
- raise NotImplementedError()
-
- def fetch_meta(self, key):
- """
- Fetches story meta information.
-
- Args:
- key: Primary key of the story.
-
- Returns:
- dict: Story meta information.
-
- Raises:
- InvalidStoryError: If a valid story is not found.
- StorySourceError: If source does not return any data.
- """
- raise NotImplementedError()
-
-
-class FimfictionFetcher(Fetcher):
- """
- Fetcher for Fimfiction.
- """
- prefetch_meta = True
- prefetch_data = False
-
- data_path = 'https://www.fimfiction.net/story/download/{}/html'
- meta_path = 'https://www.fimfiction.net/api/story.php?story={}'
-
- flavors = frozenset((
- StorySource.FIMFICTION,
- DataFormat.HTML,
- MetaPurity.DIRTY,
- ))
-
- def get(self, url):
- """
- Performs an HTTP GET request.
-
- Args:
- url: Target of the HTTP request.
-
- Returns:
- Response: A new `Response` object.
-
- Raises:
- InvalidStoryError: If access to the resource was denied.
- StorySourceError: If the request fails for any other reason.
- """
- try:
- response = requests.get(url, timeout=60)
- except OSError as e:
- raise StorySourceError("Could not read from server.") from e
-
- if response.status_code == 403:
- raise InvalidStoryError("Access to resource was denied.")
-
- if not response.ok:
- raise StorySourceError(
- "Server responded with HTTP {} {}."
- .format(response.status_code, response.reason)
- )
-
- return response
-
- def fetch_data(self, key):
- url = self.data_path.format(key)
- response = self.get(url)
- data = response.content
-
- if len(data) == 0:
- raise InvalidStoryError("Server returned empty response body.")
-
- if b'
' not in data:
- raise InvalidStoryError("Server did not return any chapters.")
-
- if not data.endswith(b'