├── src
├── __init__.py
├── gui
│ ├── __init__.py
│ ├── imgServe.py
│ ├── fetchData.py
│ └── gui.py
├── gameList.py
├── usrOps.py
├── egResponse.json
└── gameOps.py
├── plei.png
├── data
├── dirList.plei
└── gameList.plei
├── resources
├── Plei.ico
├── close.png
├── minus.png
├── xbox.png
├── pacman.png
├── tic-tac-toe.png
└── credits.txt
├── app.py
├── requirements.txt
├── .gitignore
├── README.md
└── LICENSE
/src/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/gui/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/plei.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/plei.png
--------------------------------------------------------------------------------
/data/dirList.plei:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/data/dirList.plei
--------------------------------------------------------------------------------
/data/gameList.plei:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/data/gameList.plei
--------------------------------------------------------------------------------
/resources/Plei.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/resources/Plei.ico
--------------------------------------------------------------------------------
/resources/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/resources/close.png
--------------------------------------------------------------------------------
/resources/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/resources/minus.png
--------------------------------------------------------------------------------
/resources/xbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/resources/xbox.png
--------------------------------------------------------------------------------
/resources/pacman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/resources/pacman.png
--------------------------------------------------------------------------------
/resources/tic-tac-toe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakshatshinde/Plei/HEAD/resources/tic-tac-toe.png
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | # from usrOps import *
2 | from src.gui.gui import plei
3 |
4 | if __name__ == "__main__":
5 | plei()
6 |
7 |
8 |
--------------------------------------------------------------------------------
/resources/credits.txt:
--------------------------------------------------------------------------------
1 | All credits for the icons in this project go to : https://www.flaticon.com/
2 | Plei Icon made by : https://www.flaticon.com/authors/freepik
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | nested-lookup==0.2.19
2 | pip-tools==4.3.0
3 | pipreqs==0.4.12
4 | python-dotenv==0.10.3
5 | six==1.12.0
6 | steamfiles==0.1.4
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/gameList.py:
--------------------------------------------------------------------------------
1 | import enum
2 |
3 | class Game:
4 | def __init__(self, name, gameId, gameStore):
5 | self.name = name
6 | self.gameId = gameId
7 | self.gameStore = gameStore
8 |
9 | class GameStore(enum.Enum):
10 | STEAM = "steam://rungameid/"
11 | EGS = "com.epicgames.launcher://apps/" # + ?action=launch&silent=true"
12 | UPLAY = "uplay://launch/" # launch/ID/0
13 | ORIGIN = "origin://launchgame/"
14 | STANDALONE = "STANDALONE"
15 |
16 |
--------------------------------------------------------------------------------
/src/gui/imgServe.py:
--------------------------------------------------------------------------------
1 | import urllib.request
2 |
3 | def retrieveImg(gameName, imgURL):
4 | gameName = legalFileName(gameName) # Windows doesn't like weird stuff in filenames
5 | location = 'data\\img\\'
6 | urllib.request.urlretrieve(imgURL, (location + gameName + ".png"))
7 |
8 | # Making sure windows doesn't cry if : or similar characters appear in the game name
9 | def legalFileName(inString):
10 | checkList = ['<', '>', ':', '"', '/', '\\', '|', '?', '?', '*', '.', ',']
11 | for i in checkList:
12 | if i in inString:
13 | inString = inString.replace(i, '')
14 | return inString
15 |
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/visualstudiocode
2 | # Edit at https://www.gitignore.io/?templates=visualstudiocode
3 |
4 | ### VisualStudioCode ###
5 | .vscode/*
6 | !.vscode/settings.json
7 | !.vscode/tasks.json
8 | !.vscode/launch.json
9 | !.vscode/extensions.json
10 |
11 | ### VisualStudioCode Patch ###
12 | # Ignore all local history of files
13 | .history
14 |
15 | ### Python ###
16 | # Byte-compiled / optimized / DLL files
17 | __pycache__/
18 | *.py[cod]
19 | *$py.class
20 |
21 | ### ignore ###
22 | data/
23 | build/
24 | *.rar
25 | *.plei
26 | *.log
27 | *.env
28 | # End of https://www.gitignore.io/api/visualstudiocode
29 |
30 |
--------------------------------------------------------------------------------
/src/gui/fetchData.py:
--------------------------------------------------------------------------------
1 | # This DATA is grabbed from GIANT BOMB
2 | # Vist them at : https://www.giantbomb.com/
3 | # import json
4 |
5 | import pybomb, os
6 | from nested_lookup import nested_lookup
7 | from dotenv import load_dotenv
8 | from imgServe import retrieveImg
9 |
10 | # Client
11 | load_dotenv() # secure way to access API keys
12 | API_KEY = os.getenv('API_KEY')
13 | client = pybomb.GamesClient(API_KEY)
14 |
15 | # Funcs
16 | def gameSearch(gameName: str):
17 | response = client.quick_search(
18 | name = gameName,
19 | platform = pybomb.PC,
20 | sort_by = 'original_release_date',
21 | desc = False
22 | )
23 | return response.results
24 |
25 | def gameImgSearch(gameName: str):
26 | query = gameSearch(gameName)
27 | try :
28 | result = nested_lookup("medium_url", query)[0] # returns a list
29 | return result
30 | except :
31 | raise Exception('Game not found')
32 | # print(gameImgSearch('Star Wars Jedi: fallen order'))
33 |
34 | def gameRating(gameName: str):
35 | query = gameSearch(gameName)
36 | try :
37 | result = nested_lookup("original_game_rating", query)[0][0] # returns a dict
38 | rating = nested_lookup("name", result)[0] # extracting data
39 | return rating
40 | except :
41 | rating = 'None'
42 | return rating
43 | # print(gameRating('Warframe'))
44 |
45 | def imageCache(gameName: str):
46 | imgURL = gameImgSearch(gameName)
47 | retrieveImg(gameName, imgURL)
48 | # imageCache('Star Wars Jedi: fallen order') #// look in gui/imgServe
49 |
50 |
--------------------------------------------------------------------------------
/src/usrOps.py:
--------------------------------------------------------------------------------
1 | import pickle
2 |
3 | # Read the dirList data file and dump into a dict
4 | def readDirList():
5 | with open('data\\dirList.plei', 'rb') as f:
6 | # print(pickle.load(f))
7 | return pickle.load(f) #returns dict
8 |
9 | GAME_DIRS = readDirList()
10 | # Storing directories of game stores
11 |
12 | global originPath, uPlayPath, steamPath, standalonePath, egsPath
13 |
14 | def storeDirectory(gameStore, storeDir) -> str:
15 |
16 | if(gameStore == "ORIGIN"):
17 | originPath = storeDir
18 | GAME_DIRS[gameStore] = originPath
19 | return(originPath)
20 |
21 | if(gameStore == "UPLAY"):
22 | uPlayPath = storeDir
23 | GAME_DIRS[gameStore] = uPlayPath
24 | return(uPlayPath)
25 |
26 | if(gameStore == "STANDALONE"):
27 | standalonePath = storeDir
28 | GAME_DIRS[gameStore] = standalonePath
29 | return(standalonePath)
30 |
31 | if(gameStore == "STEAM"):
32 | steamPath = storeDir
33 | GAME_DIRS[gameStore] = steamPath
34 | return(steamPath)
35 |
36 | if(gameStore == "EGS"):
37 | egsPath = storeDir
38 | GAME_DIRS[gameStore] = egsPath
39 | return(egsPath)
40 |
41 | # storeDirectory('STEAM','C:\\Program Files (x86)\\Origin Games')
42 | # print(GAME_DIRS)
43 |
44 |
45 | def selection():
46 | while True:
47 | print("Select a game store")
48 | sel = int(input("\n 1: STEAM \n 2: EGS \n 3: ORIGIN \n 4: UPLAY \n 5: Standalone \n 6: Done \n\n"))
49 | dirLoc = str(input("Input the directory location"))
50 |
51 | if sel == 1: storeDirectory('STEAM', dirLoc)
52 | if sel == 2: storeDirectory('EGS', dirLoc)
53 | if sel == 3: storeDirectory('ORIGIN', dirLoc)
54 | if sel == 4: storeDirectory('UPLAY', dirLoc)
55 | if sel == 5: storeDirectory('Standalone', dirLoc)
56 | if sel == 6: return 'We are all set!!'
57 |
58 |
59 | # Update the dirList data file
60 | def writeDirList(dirList: dict):
61 | with open('data\\dirList.plei', 'wb') as f:
62 | pickle.dump(dirList, f)
63 |
64 | # print(readDirList())
65 | # selection()
66 | # writeDirList(GAME_DIRS)
67 | # print(readDirList())
--------------------------------------------------------------------------------
/src/egResponse.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "aliases": null,
4 | "api_detail_url": "https://www.giantbomb.com/api/game/3030-72014/",
5 | "date_added": "2019-02-03 18:57:47",
6 | "date_last_updated": "2020-01-12 19:15:11",
7 | "deck": "A free-to-play battle royale spin-off of the Titanfall series, trading the series' trademark mechs for class-based last-squad-standing gameplay.",
8 | "expected_release_day": 4,
9 | "expected_release_month": 2,
10 | "expected_release_quarter": null,
11 | "expected_release_year": 2019,
12 | "guid": "3030-72014",
13 | "id": 72014,
14 | "image": {
15 | "icon_url": "https://www.giantbomb.com/api/image/square_avatar/3079288-box_al.png",
16 | "image_tags": "All Images",
17 | "medium_url": "https://www.giantbomb.com/api/image/scale_medium/3079288-box_al.png",
18 | "original_url": "https://www.giantbomb.com/api/image/original/3079288-box_al.png",
19 | "screen_large_url": "https://www.giantbomb.com/api/image/screen_kubrick/3079288-box_al.png",
20 | "screen_url": "https://www.giantbomb.com/api/image/screen_medium/3079288-box_al.png",
21 | "small_url": "https://www.giantbomb.com/api/image/scale_small/3079288-box_al.png",
22 | "super_url": "https://www.giantbomb.com/api/image/scale_large/3079288-box_al.png",
23 | "thumb_url": "https://www.giantbomb.com/api/image/scale_avatar/3079288-box_al.png",
24 | "tiny_url": "https://www.giantbomb.com/api/image/square_mini/3079288-box_al.png"
25 | },
26 | "image_tags": [
27 | {
28 | "api_detail_url": "https://www.giantbomb.com/api/images/3030-72014/?filter=image_tag%3AAll+Images",
29 | "name": "All Images",
30 | "total": 28
31 | },
32 | {
33 | "api_detail_url": "https://www.giantbomb.com/api/images/3030-72014/?filter=image_tag%3ABox+Art",
34 | "name": "Box Art",
35 | "total": 3
36 | },
37 | {
38 | "api_detail_url": "https://www.giantbomb.com/api/images/3030-72014/?filter=image_tag%3AScreenshots",
39 | "name": "Screenshots",
40 | "total": 22
41 | }
42 | ],
43 | "name": "Apex Legends",
44 | "number_of_user_reviews": 0,
45 | "original_game_rating": [
46 | {
47 | "api_detail_url": "https://www.giantbomb.com/api/game_rating/3065-1/",
48 | "id": 1,
49 | "name": "ESRB: T"
50 | }
51 | ],
52 | "original_release_date": null,
53 | "platforms": [
54 | {
55 | "abbreviation": "PC",
56 | "api_detail_url": "https://www.giantbomb.com/api/platform/3045-94/",
57 | "id": 94,
58 | "name": "PC",
59 | "site_detail_url": "https://www.giantbomb.com/pc/3045-94/"
60 | },
61 | {
62 | "abbreviation": "XONE",
63 | "api_detail_url": "https://www.giantbomb.com/api/platform/3045-145/",
64 | "id": 145,
65 | "name": "Xbox One",
66 | "site_detail_url": "https://www.giantbomb.com/xbox-one/3045-145/"
67 | },
68 | {
69 | "abbreviation": "PS4",
70 | "api_detail_url": "https://www.giantbomb.com/api/platform/3045-146/",
71 | "id": 146,
72 | "name": "PlayStation 4",
73 | "site_detail_url": "https://www.giantbomb.com/playstation-4/3045-146/"
74 | }
75 | ],
76 | "site_detail_url": "https://www.giantbomb.com/apex-legends/3030-72014/"
77 | }
78 | ]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Plei
3 | **A game launcher with no bloat**
4 |
5 | [](https://www.python.org/)
6 | [](http://www.gnu.org/licenses/gpl-3.0.en.html)
7 |
8 | > **Warning**
9 | > **Install might break due to:** https://github.com/leovp/steamfiles/pull/20
10 |
11 |
12 | **This project is ready for the launchers given below!**
13 |
14 | - [x] Steam
15 | - [x] Origin
16 | - [x] Epic Games Launcher
17 | - [x] Uplay
18 | - [ ] Standalone Games (Not gonna do this anytime soon :c)
19 |
20 | ---
21 |
22 | ## Why is this a thing?
23 |
24 | As new game launchers came into being it increasingly got annoying to keep track of all the games over different stores/platform. I wanted to make a unified front where users can access all their games, including the one with no launchers. The goal behind this launcher is to be simple, minimilistic and **bloat-free**
25 |
26 | ---
27 |
28 | ## How to install? (For Devs)
29 |
30 | #### This installation assumed you have python 3.X installed. [Get python here](https://www.python.org/ftp/python/3.8.1/python-3.8.1-amd64.exe)
31 | - Download [Plei](https://github.com/sakshatshinde/Plei/archive/master.zip) and extract it to your desired location
32 | - Run the following command
33 | > pip install -r requirements.txt
34 | - It might error out on "steamfiles" install. To fix this change your pip version to 9.0.X and run the above command again
35 | > python -m pip install pip==9.0.3
36 | - app.pyw will launch Plei
37 | - Rename app.pyw to app.py to see the console message for logging/testing
38 |
39 | ---
40 |
41 |
42 | ## How to use it?
43 |
44 | - When asked, the user must point the appropriate path to your game libraries. As in when asked for Steam point it to your Steam library (steamapps folder)
45 | - If you have all your libraries in their default position no need to set them up again. Eg: `C:\Program Files(x86)\Origin Games\`
46 | - The above mentioned process maybe fully automated in the future
47 | - After the initial setup you will be greeted with the installed games, just click on one to play it. Its that simple!
48 |
49 | ---
50 |
51 | ## Future endeavors
52 |
53 | - Game recommendations based on your current library
54 | - Statistics about your gaming habbits
55 |
56 | ---
57 |
58 | ## Why is this launcher dependent on other launchers?
59 |
60 | Sadly, Some games need the parent launcher to be running in order to launch them. If you try to launch the game from it's binary `exe` it may refuse to launch. To keep things simple, compatible and working as intended, this design decision had to be made. If anyone finds a workaround I'd be happy to implement it, you may contribute to the project directly as well.
61 |
62 | ---
63 |
64 | :triangular_ruler: :pencil2: :straight_ruler:
65 | ## Diagram
66 |
67 | [](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggTFJcblxuQT5QbGVpXSAtLT4gU3t7U3RlYW19fVxuQSAtLT4gRXt7RUdTfX1cbkEgLS0-IE97e09yaWdpbn19XG5BIC0tPiBVe3t1UGxheX19XG5BIC0tPiBCe3tiYXR0bGVOZXR9fVxuQSAtLT4gUFtPZmZsaW5lIEdhbWVzXVxuUyAtLT4gTihTdG9yZSBOZXR3b3JrIFByb3RvY29sKVxuRSAtLT4gTihTdG9yZSBOZXR3b3JrIFByb3RvY29sKVxuTyAtLT4gTihTdG9yZSBOZXR3b3JrIFByb3RvY29sKVxuVSAtLT4gTihTdG9yZSBOZXR3b3JrIFByb3RvY29sKVxuQiAtLT4gTihTdG9yZSBOZXR3b3JrIFByb3RvY29sKVxuUCAtLT4gYmluKEJpbmFyeSBFWEUpXG5OIC0uLT4gTChbTGF1bmNoIHRoZSBnYW1lXSlcbmJpbiAtLi0-IEwiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ)
68 |
69 | ---
70 |
71 |
--------------------------------------------------------------------------------
/src/gui/gui.py:
--------------------------------------------------------------------------------
1 | def plei():
2 | import tkinter as tk
3 | from tkinter import ttk
4 | import tkinter.font as fontyPoo # Sorry for the weird name :p
5 | import winsound, pickle
6 | from src.gameOps import readData, sync, launchGame
7 | # import fetchData
8 |
9 | BG_VAL = "#212121"
10 | FG_VAL = "#D2BF55"
11 |
12 | def close(): #Close the window
13 | root.destroy()
14 |
15 | def minWin(): #minimze the window
16 | root.withdraw()
17 | root.overrideredirect(False)
18 | root.iconify()
19 |
20 | def check_map(event): # apply override on deiconify.
21 | if str(event) == "