Ejemplo n.º 1
0
def add_mod(collection_id, mod_id):
    """
    Adds a mod to a collection
    :param collection_id: int
    :param mod_id: int
    :return: None
    """
    try:
        collection_id = int(collection_id)
    except ValueError:
        logger.critical("Collection id is not a valid integer")
        raise

    try:
        mod_id = int(mod_id)
    except ValueError:
        logger.critical("Mod id is not a valid integer")
        raise

    logger.info("Adding mod {} to collection {}".format(mod_id, collection_id))
    coll = Collection.query.get(collection_id)
    mod = Mod.query.get(mod_id)

    if mod is not None and coll is not None:
        logger.info("Beginning addition...")

        # 1. Create symlink
        logger.info("Creating Symbolic link...")
        try:
            create_symbolic_link(
                join_path(config["application_root"], "mods", mod.filename),
                join_path(config["application_root"], "collections",
                          coll.mc_id.decode(), "mods",
                          get_file_name(mod.filename)))
        except FileExistsError:
            logger.warning("File already exists, continuing...")

        logger.info("Symlink created")

        # 2. Add relationship to database, for all mods in this file
        for m in Mod.query.filter(Mod.filename == mod.filename):
            logger.debug(
                "Adding mod: {!r} to collection: {!r} in database".format(
                    m, coll))
            coll.add_mod(m)
        db_session.commit()
        logger.info("Mod added successfully")
    elif mod is None:
        logger.error("Mod does not exist")
        raise exceptions.ModNotExistError(mod_id)
    else:
        logger.error("Collection does not exist")
        raise exceptions.CollectionNotExistError(collection_id)
Ejemplo n.º 2
0
    def commit(self,
               launcher_profile_loc=join_path(config["minecraft_directory"],
                                              "launcher_profiles.json")):
        """
        Saves profile to launcher_profiles.json, overwriting what previously had the same id as this
        :param launcher_profile_loc: string, absolute path to the launcher_profiles.json
        :return: None
        """
        with open(launcher_profile_loc) as data_file:
            tmp = json.load(data_file)

        tmp["profiles"][self.id.decode()] = self.dictify()

        with open(join_path(launcher_profile_loc), 'w') as data_file:
            json.dump(tmp, data_file)
Ejemplo n.º 3
0
    def __init__(self, loc):
        self.loc = loc

        self.json_loc = glob.glob(join_path(loc, "**.json"))[0]
        print(self.json_loc, end="\n\n")
        with open(self.json_loc) as x:
            self.info = json.load(x)
Ejemplo n.º 4
0
def open_file_browser(id):
    c = getcoll(id)
    util.open_file_browser(
        util.join_path(config["application_root"], "collections",
                       c.mc_id.decode()))

    return jsonify(dict(status="success"))
Ejemplo n.º 5
0
def get_profiles():
    """
    Gets all of the profiles which are currently in launcher_profiles.json
    :return: list<dict>
    """
    with open(
            join_path(config["minecraft_directory"],
                      "launcher_profiles.json")) as p:
        profile_json = json.load(p)["profiles"]

    #pprint.pprint(profile_json)

    profiles = []

    for id, data in profile_json.items():
        #print("{}: {}".format(id, data))
        if id in Collection.query.filter(Collection.mc_id).all(
        ):  # if the id is the same as the id of one in our db
            print("{}: {}".format(id, data))
            # it's one of ours:
            profiles.append(
                Profile(id, data["name"], data["lastVersionId"],
                        data["gameDir"], data["type"], data["created"],
                        data["lastUsed"]))

    return profiles
Ejemplo n.º 6
0
 def get_collection_path(self):
     """
     Returns an absolute path to the collection's directory
     :return: string
     """
     return util.join_path(config["application_root"], "collections",
                           self.mc_id.decode())
Ejemplo n.º 7
0
def rem_profile(id,
                launcher_profile_loc=join_path(config["minecraft_directory"],
                                               "launcher_profiles.json")):
    """
    Removes a Profile from it's id
    :param id: string
    :return: None
    """
    with open(launcher_profile_loc) as data_file:
        tmp = json.load(data_file)

    try:
        del tmp["profiles"][id.decode()]
    except KeyError:
        pass

    with open(join_path(launcher_profile_loc), 'w') as data_file:
        json.dump(tmp, data_file)
Ejemplo n.º 8
0
    def remove(self,
               launcher_profile_loc=join_path(config["minecraft_directory"],
                                              "launcher_profiles.json")):
        """
        Removes Profile from launcher_profiles.json
        :param launcher_profile_loc: string, absolute path to the launcher_profiles.json
        :return: None
        """
        with open(launcher_profile_loc) as data_file:
            tmp = json.load(data_file)

        try:
            del tmp["profiles"][self.id.decode()]
        except KeyError:
            pass

        with open(join_path(launcher_profile_loc), 'w') as data_file:
            json.dump(tmp, data_file)
Ejemplo n.º 9
0
def _get_curse_mods():
    """
    Entrypoint for getting and parsing Curse mods. This should be started in a new thread.
    :return: None
    """
    dl_folder = util.join_path(
        config["application_root"],
        "tmp",
        "cursedata",
    )

    dl_loc = util.join_path(dl_folder, "cursedata.json.bz2")

    json_loc = util.join_path(dl_folder, "cursedata.json")

    if not os.path.exists(dl_folder):
        logger.warning(
            "Directory: {} doesn't exist yet, creating...".format(dl_folder))
        os.makedirs(dl_folder)

    logger.info("Getting Curse mods...")
    try:
        _download_json(dl_loc)
    except Exception:
        logger.error(
            "Moddata download failed, maybe the computer is offline. Some Advanced features will be unavailable until reconnection occurs"
        )
        return

    logger.info("Extracting archive...")
    _extract_json(dl_loc, json_loc)

    logger.info("Deleting archive...")
    try:
        os.unlink(dl_loc)
    except Exception as ex:
        logger.error("File Unlink failed with error: {}: {}".format(
            type(ex).__class__, ex.args))
    else:
        logger.info("Archive deletion complete")

    logger.info("Parsing extracted curse mod data...")
    _parse_moddata(json_loc)
Ejemplo n.º 10
0
    def __init__(self, config_loc=join_path(dirname(abspath(__file__)), 'config.json')):
        self.config_loc = config_loc

        if not os.path.exists(self.config_loc):
            print("Config file doesn't exist. Creating...")
            with open(self.config_loc, 'w') as f:
                json.dump(DEFAULT_CONFIG, f)

        with open(self.config_loc) as config_file:
            self.json = json.load(config_file)
Ejemplo n.º 11
0
def get_versions():
    """
    Gets all of the valid versions, which are currently installed
    :return: list<Version>
    """
    versions = []
    for jsonfile in glob.glob(
            join_path(config["minecraft_directory"], "versions", "*", "")):
        versions.append(Version(jsonfile))
    return versions
Ejemplo n.º 12
0
def rem_mod(collection_id, mod_id):
    """
    Removes a mod from the collection
    :param collection_id: int, the id of the collection
    :param mod_id: int, id of the mod
    :return: None
    """
    try:
        collection_id = int(collection_id)
    except ValueError:
        logger.critical("Collection id is not a valid integer")
        raise

    try:
        mod_id = int(mod_id)
    except ValueError:
        logger.critical("Mod id is not a valid integer")
        raise

    logger.info("Removing mod with id: {} from collection with id: {}".format(
        mod_id, collection_id))
    coll = Collection.query.get(collection_id)
    mod = Mod.query.get(mod_id)
    if mod is not None and coll is not None:
        logger.info("Mod and Collection exist, proceeding to removal...")
        if mod in coll.mods:  # check if the mod is in this collection
            logger.info("Removing symbolic link...")
            try:
                os.unlink(
                    join_path(config["application_root"], "collections",
                              coll.mc_id.decode(), "mods",
                              get_file_name(mod.filename)))
            except FileNotFoundError:
                logger.warning("Symlink already missing, continuing...")

            logger.info("Symlink removed")

            logger.info("Removing link from database...")
            for m in Mod.query.filter(
                    and_(Mod.collections.any(id=collection_id),
                         Mod.filename == mod.filename)):
                logger.debug("Removing Mod: {!r}".format(m))
                coll.rem_mod(m)
            db_session.commit()
            logger.info("Database commit successful")
        else:
            logger.error("Mod is not in this collection, no action required")

        logger.info("Mod removal successful")
    elif mod is None:
        logger.error("Mod does not exist")
        raise exceptions.ModNotExistError(mod_id)
    else:
        logger.error("Collection doesn't exist")
        raise exceptions.CollectionNotExistError(collection_id)
Ejemplo n.º 13
0
def upload_mod():
    """
    Ability to upload a mod file to be added to the mod pool
    :return:
    """
    if request.method == 'POST':
        logger.info("Got mod upload request with files: {}".format(
            request.files))

        # check there's a file:
        if 'file' not in request.files:
            logger.error("No file part found")
            return jsonify(
                dict(status="error",
                     title="No mod to upload",
                     body="Please select a mod file to upload.",
                     code="InvalidFile"))

        file = request.files['file']

        logger.info("Got file: {} with filename: `{}`".format(
            file, file.filename))

        if file.filename == '':
            logger.error("file.filename is empty")
            return jsonify(
                dict(status="error",
                     title="No mod to upload",
                     body="Please select a mod file to upload.",
                     code="InvalidFile"))
        if file and m.allowed_modfile(file.filename):
            filename = secure_filename(file.filename)
            temp_path = join_path(UPLOADS_DIR, filename)

            if not os.path.exists(UPLOADS_DIR):
                os.makedirs(UPLOADS_DIR)

            file.save(temp_path)

            try:
                modid = m.add(temp_path,
                              ignore_modexist_error=resolve_url_bool(
                                  request.args.get("overwrite")))
            except exceptions.ModExistsError:
                return jsonify(
                    dict(status="error",
                         title="No mods found",
                         body="Please select a valid mod file to upload.",
                         code="ModExistsError"
                         # try again with arg ignore_modexist_error=True
                         ))
            else:
                return jsonify(dict(status="success", modid=modid))
Ejemplo n.º 14
0
def get_version_names():
    """
    Gets all of the valid versions, which are currently installed
    :return: list<String>
    """
    versions = []
    for jsonfile in glob.glob(
            join_path(config["minecraft_directory"], "versions", "*",
                      "*.json")):
        with open(jsonfile) as x:
            j = json.load(x)
            versions.append(j["id"])

    return versions
Ejemplo n.º 15
0
def curse_mod():
    """
    Download a mod from Curse, populating it's metadata in the database
    :return:
    """
    if request.method == 'POST':
        if not request.args.get("curse_id"):
            abort(400)

        if not request.args.get("file_id"):
            abort(400)

        if not request.args.get("url"):
            abort(400)

        # get download url
        url = request.args.get("url")
        try:
            # download the mod
            temp_path = join_path(config["application_root"], "tmp", "uploads",
                                  urlsplit(url).path.split('/')[-1])

            download_to_file(url, temp_path)

            try:
                return jsonify(
                    dict(status='success',
                         modid=m.add(temp_path,
                                     url,
                                     curse_id=request.args.get("curse_id"),
                                     curse_file_id=request.args.get("file_id"),
                                     ignore_modexist_error=resolve_url_bool(
                                         request.args.get("overwrite")))))
            except exceptions.ModExistsError:
                return jsonify(
                    dict(status='error',
                         title="Mod already exists",
                         body="Please select a valid mod file to download.",
                         code="ModExistsError"))

        except HTTPError as e:
            logger.error("Failed to download Modfile: {} ({} - {})".format(
                e.filename, type(e), e))
            abort(e.code)
        except URLError as e:
            logger.error("Failed to download ModFile: {}".format(e.filename))
            abort(400)
        except Exception:
            raise
Ejemplo n.º 16
0
def _create_collection_filesystem(root,
                                  mc_id,
                                  folders=("mods", "config", "saves",
                                           "resourcepacks")):
    """
    Creates the filesystem for a Collection
    :param root: string, the location which contains all Collections' folders
    :param mc_id: string, the mc_id of the collection
    :param folders: tuple<string>, all of the subfolders to create for the collection
    :return: string, absolute path to the collection's root folder
    """
    # create the path up to <root>/<name>/
    coll_root = join_path(root, mc_id.decode())
    print("Creating Folder: {}".format(coll_root))

    if os.path.exists(coll_root):
        shutil.rmtree(coll_root)
    os.makedirs(coll_root)

    for folder in folders:
        # create all of the sub folders for this collection
        os.mkdir(join_path(coll_root, folder))

    return coll_root  # e.g. /tfff1/SimpleLauncher/collections/<name> or C:\tfff1\SimpleLauncher\collections\<name>
Ejemplo n.º 17
0
def add(name, mcversion, version_id):
    """
    Adds a new collection (if it doesn't exist already) to the DB, Minecraft, & the FileSystem
    :param name: string, user-friendly name of the Collection
    :param mcversion: string, general Minecraft Version for the collection. e.g. 1.7.10
    :param version_id: string, exact name of a specific version of minecraft. e.g. 1.7.10-Forge10.13.4.1558-1.7.10
    :return: int, id of the newly created Collection
    """
    logger.info("Adding collection: {}, {}, {}".format(name, mcversion,
                                                       version_id))
    if not name_exists(
            name):  # if a Collection by this name doesn't yet exist:
        # 1. Add it to the database
        logger.info("Adding to database")
        c = Collection(name, mcversion, version_id)
        db_session.add(c)
        db_session.commit()
        logger.info("Committed to database")

        # 2. Creates it's directory and filesystem
        logger.info("Creating filesystem...")
        coll_loc = _create_collection_filesystem(
            join_path(config["application_root"], "collections"), c.mc_id)
        logger.info("Filesystem created")

        # 3. Add it to Minecraft as a Profile
        logger.info("Adding Minecraft Profile")
        prof = mc_interface.Profile(c.mc_id, name, version_id, coll_loc)
        prof.commit()
        logger.info("New Profile committed")

        print(
            mc_interface.Profile(c.mc_id, name, version_id,
                                 coll_loc).dictify())

        return c.id
    else:
        logger.error(
            "A Collection with the name: {} already exists".format(name))
        raise exceptions.CollectionExistsError(
            str(Collection.query.filter(Collection.name == name).first().id))
Ejemplo n.º 18
0
def update_1_0_to_1_1(config_file_path):
    # type: (str) -> str
    print("upgrading from version 1.0 to 1.1...")
    logging.debug("upgrading from version 1.0 to 1.1...")
    with open(config_file_path, 'r') as f:
        config = json.load(f)

    # add new values (set to default)
    config["database_path"] = join_path(config["application_root"],
                                        "moddata.sqlite")
    config["webserver_port"] = 4000

    # remove unneeded values

    # update modified values
    config["version"] = "1.1"

    # write out again
    with open(config_file_path, 'w') as f:
        json.dump(config, f)

    return "1.1"
Ejemplo n.º 19
0
def download_mod():
    """
    Ability to download a mod file to be added to the mod pool, from a direct download link
    :return:
    """
    if request.method == 'POST':
        if not request.args.get("url"):
            abort(400)
        else:
            try:
                temp_path = join_path(
                    config["application_root"], "tmp", "uploads",
                    urlsplit(request.args.get("url")).path.split('/')[-1])

                download_to_file(request.args.get("url"), temp_path)
                try:
                    return redirect("/mod/{}".format(
                        m.add(temp_path,
                              request.args.get("url"),
                              ignore_modexist_error=resolve_url_bool(
                                  request.args.get("overwrite")))))
                except exceptions.ModExistsError:
                    return render_template('confirm.html',
                                           title="Confirmation required",
                                           desc="")

            except HTTPError as e:
                logger.error("Failed to download Modfile: {}".format(
                    e.filename))
                abort(e.code)
            except URLError as e:
                logger.error("Failed to download ModFile: {}".format(
                    e.filename))
                abort(400)
            except Exception:
                raise
Ejemplo n.º 20
0
def _add_after_infoparse(file,
                         info,
                         dl_url,
                         ignore_modexist_error=False,
                         curse_id=None,
                         curse_file_id=None):
    """
    Does things after parsing of the file
    :param file: string, absolute path to the modfile (located somewhere temporarily, in APPLICATION_ROOT/tmp/uploads/<file>)
    :param info: MCModFile
    :param dl_url: string, URL the mod was downloaded from, if it was acquired via direct DL
    :param ignore_modexist_error: bool, whether or not to ignore the ModExistsError
    :return: int, id of the mod
    """
    # 1. Move the file in to the mod folder

    # i. ensure that the directory exists
    logger.info("Creating directories as required")
    os.makedirs(join_path(config["application_root"], "mods"), exist_ok=True)

    # ii. Check if the file already exists:
    final_filename = get_file_name(file)
    final_filepath = join_path(config["application_root"], "mods",
                               final_filename)
    logger.info(
        "Determined path for mod file to be: {}".format(final_filepath))

    if os.path.exists(final_filepath):
        try:
            logger.warning("Final path exists! Figuring out what to do...")
            # it is already present in the directory. Compare the files
            logger.debug("Found filename of path: {} to be: {}".format(
                file, get_file_name(file)))
            for existing in Mod.query.filter(
                    or_(Mod.filename == get_file_name(file),
                        Mod.modid.in_([i.modid for i in info.mods]))).all():
                # for every mod with the same filename, or with the same modid.
                #logger.debug("Found matching mod in database: {}".format(existing))
                for mod in info.mods:
                    logger.debug(
                        "Checking existing mod: {} against {} found in modfile"
                        .format(existing, mod))
                    if existing.modid == mod.modid and existing.version == mod.version:
                        # they are the same mod, at the same version. Do they want a double up?
                        logger.error(
                            "Mod file has the same modid, at the same version")
                        raise exceptions.ModExistsError(existing.id)
                else:
                    continue
            else:
                logger.info(
                    "No exact match for name and version exists in our database. Generating a unique file name..."
                )
                # they're different. Fix the naming thing
                final_filename = find_unique_name(
                    join_path(config["application_root"], "mods"),
                    get_file_name(file))

                final_filepath = join_path(config["application_root"], "mods",
                                           final_filename)

        except exceptions.ModExistsError:
            if not ignore_modexist_error:
                # clean up the mod file from temp
                logger.warning(
                    "ignore_modexist_error flag set to False, so cancelling operation."
                )
                try:
                    logger.debug(
                        "Attempting to clean up modfile at: {}".format(file))
                    os.unlink(file)
                except OSError:
                    logger.warning(
                        "Failed to clean modfile at: {}".format(file))
                    pass

                raise
            else:
                # We think that this is a duplicate, but we've been told to add it anyway so, generate new filename
                logger.info(
                    "ignore_modexist_error flag set to True, so continuing operation with renamed file."
                )
                # fix the file naming thing
                final_filename = find_unique_name(
                    join_path(config["application_root"], "mods"),
                    get_file_name(file))

                final_filepath = join_path(config["application_root"], "mods",
                                           final_filename)

    # iii. perform the move
    logger.info("Moving {} to {}".format(file, final_filepath))
    shutil.move(  # shutil because it can handle operations across disks (e.g. C drive to F drive) unlike os.rename
        file, final_filepath)
    logger.info("Move successful. Adding mods to database...")

    # 2. Add to db
    if info.mods:
        logger.debug("Mods present, beginning add...")
        info.add_to_database(final_filename,
                             dl_url,
                             curse_id=None,
                             curse_file_id=None)
    else:
        logger.error("No Mods are found in the mod file")
        raise exceptions.ModNotExistError()

    logger.info("Finished adding mod file.")

    return Mod.query.order_by(Mod.id.desc()).first().id
Ejemplo n.º 21
0
from os.path import dirname, abspath
import logging

logger = logging.getLogger(__name__)

VERSIONS = ["1.0", "1.1"]
"""
These functions migrate from the key version up to the config version specified by the function's return value.
"""
CONFIG_UPDATERS = {
    "1.0": lambda: None,
}

DEFAULT_CONFIG = {
    "version": "1.0",
    "application_root": join_path(get_default_tfff1_loc(), 'SimpleLauncher'),
    "minecraft_directory": get_default_mc_loc(),
    "fetch_curse_moddata": True,
    "logging": {
        "version": 1,
        "formatters": {
            "f": {
                "format": "(%(name)s) [%(levelname)s]: %(message)s"
            }
        },
        "handlers": {
            'fh': {
                'class': 'logging.handlers.RotatingFileHandler',
                'formatter': 'f',
                'level': logging.INFO,
                'maxBytes': 1000000,
Ejemplo n.º 22
0
def remove(id):
    """
    Removes a mod from the filesystem and database, including deletion of all links to collections
    :param id: int
    :return: None
    """
    logger.info("Beginning removal of mod with id: {}".format(id))

    # 0. Get file name:
    logger.debug("Getting Mod from database...")
    m = Mod.query.get(id)

    if m is None:
        logger.error("Mod doesn't exist in database")
        raise exceptions.ModNotExistError(id)

    filename = m.filename

    if filename is None:
        logger.critical(
            "Database entry is missing mission-critical filename data. Throwing error..."
        )
        raise exceptions.ModInfoNotExistError(id, 'filename')

    # 1. Remove all symlinks in Collections
    logger.info("Removing from collections: {}".format(m.collections))
    for coll in m.collections:
        logger.debug("Working with collection {}...".format(coll.id))
        logger.debug("Removing Symbolic link...")
        try:
            os.unlink(
                join_path(config["application_root"], "collections",
                          coll.mc_id.decode(), "mods", filename))
        except FileNotFoundError as ex:
            logger.error("Symlink doesn't exist")
        except OSError as ex:
            logger.critical(
                "Insufficient Permissions for Symbolic link manipulation: {}".
                format(ex.args))

        logger.debug("Symlink removed successfully")

        logger.debug("Removing relationship...")
        coll.rem_mod(m)
        logger.debug("Relationship removed")

    logger.info("Finished removing from collections")

    # 2. Delete mod file:
    logger.info("Deleting mod file...")
    os.remove(join_path(config["application_root"], "mods", filename))
    logger.info("Deletion successful")

    # 3. Delete Mod from database
    logger.info("Removing from database...")
    for mod in Mod.query.filter(Mod.filename == m.filename):
        logger.debug("Removing mod: {!r} from database".format(mod))
        db_session.delete(mod)
    logger.info("Removed")

    logger.debug("Committing Database changes...")
    db_session.commit()
    logger.debug("Commit successful")

    logger.info("Finished Removing mod with id {}".format(id))
Ejemplo n.º 23
0
import os
from os.path import dirname, abspath, isfile
import logging
from .migrator import CONFIG_UPDATERS


class ConfigUpdateError(Exception):
    pass


logger = logging.getLogger(__name__)

VERSIONS = ["1.0", "1.1"]
HISTORIC_CONFIG_LOCATIONS = [  # the zero'th item should be the current location
    # from latest to oldest
    join_path(get_default_tfff1_loc(), "SimpleLauncher", "config.json"),
    join_path(dirname(dirname(abspath(__file__))), "config.json")
]

DEFAULT_CONFIG = {
    "version":
    "1.1",
    "application_root":
    join_path(get_default_tfff1_loc(), 'SimpleLauncher'),
    "minecraft_directory":
    get_default_mc_loc(),
    "fetch_curse_moddata":
    True,
    "database_path":
    join_path(get_default_tfff1_loc(), 'SimpleLauncher', 'moddata.sqlite'),
    "webserver_port":
Ejemplo n.º 24
0
import logging
import os
from flask import Blueprint, request, flash, render_template, redirect, abort, jsonify
from werkzeug.utils import secure_filename
from simple_mod_installer import mod as m, config
from simple_mod_installer.util import join_path, resolve_url_bool
from simple_mod_installer.util.http import download_to_file
from simple_mod_installer import exceptions, searchmods
from urllib.error import URLError, HTTPError
from urllib.parse import urlsplit

mod = Blueprint('mod', __name__, template_folder='../../templates')
logger = logging.getLogger(__name__)

UPLOADS_DIR = join_path(config["application_root"], "tmp", "uploads")
'''
@mod.route('/')
def index():
    return render_template('mod_index.html', mods=m.all())


@mod.route('/<id>')
def view_mod(id):
    """
    Displays a Mod
    :param id: int, is of the mod
    """
    try:
        return render_template('mod_view.html', mod=m.get_from_id(id))
    except exceptions.ModNotExistError:  # a collection with this id doesn't exist
        abort(404)