Skip to content

Commit

Permalink
Merge branch 'dev-primetime43' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
primetime43 committed Jul 9, 2023
2 parents 19e49e3 + 0dca1a7 commit de1753d
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 73 deletions.
29 changes: 29 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Use an official Python runtime as a parent image
FROM python:3.7-slim-buster

# Set the working directory in the container to /app
WORKDIR /app

# Add the current directory contents into the container at /app
ADD . /app

# Update and install build dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libssl-dev \
libffi-dev \
python3-dev \
python3-pip

# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 5000

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["uwsgi", "--ini", "app.ini"]

69 changes: 34 additions & 35 deletions GAPS 2.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,40 +78,39 @@ def save_tmdb_key():
# If the API key is not valid, return an error message
return {'message': app.config['RESPONSE_MESSAGES']['api_key_failure'] + str(response.status_code)}, response.status_code

@app.route('/link_plex_account', methods=['POST'])
def link_plex_account():
try:
headers = {'X-Plex-Client-Identifier': app.config['PLEX_CLIENT_IDENTIFIER']}
pinlogin = MyPlexPinLogin(headers=headers, oauth=True)
oauth_url = pinlogin.oauthUrl()
webbrowser.open(oauth_url)
pinlogin.run(timeout=120)
pinlogin.waitForLogin()
if pinlogin.token:
plex_data = PlexAccountData() # Create a new PlexAccountData object
plex_account = MyPlexAccount(token=pinlogin.token)
username = plex_account.username # Get the username
resources = [resource for resource in plex_account.resources() if resource.owned and resource.connections]
servers = [f"{resource.name} ({resource.connections[0].address})" for resource in resources if resource.connections]

# Store tokens in the dictionary
for resource in resources:
if resource.connections:
server_name = f"{resource.name} ({resource.connections[0].address})"
tokens[server_name] = pinlogin.token
plex_data.add_token(server_name, pinlogin.token)

plex_data.set_servers(servers)

# Store the PlexAccountData object in the array
plex_data_array.append(plex_data)

# Return the JSON response with servers and token
return jsonify(servers=servers, token=pinlogin.token)
else:
return jsonify({'message': app.config['RESPONSE_MESSAGES']['plex_account_error'], 'servers': [], 'token': None})
except Exception as e:
return jsonify({'message': app.config['RESPONSE_MESSAGES']['plex_account_error'], 'servers': [], 'token': None})
@app.route('/fetch_servers', methods=['POST'])
def fetch_servers():
if globalPin.checkLogin(): # Assumes that checkLogin() returns True if the user is authenticated
plex_data = PlexAccountData() # Create a new PlexAccountData object
plex_account = MyPlexAccount(token=globalPin.token)
username = plex_account.username # Get the username
resources = [resource for resource in plex_account.resources() if resource.owned and resource.connections]
servers = [f"{resource.name} ({resource.connections[0].address})" for resource in resources if resource.connections]

# Store tokens in the dictionary
for resource in resources:
if resource.connections:
server_name = f"{resource.name} ({resource.connections[0].address})"
tokens[server_name] = globalPin.token
plex_data.add_token(server_name, globalPin.token)

plex_data.set_servers(servers)

# Store the PlexAccountData object in the array
plex_data_array.append(plex_data)

# Return the JSON response with servers and token
return jsonify(servers=servers, token=globalPin.token)
else:
return jsonify({'message': 'User is not authenticated', 'servers': [], 'token': None})

globalPin = None
@app.route('/authenticate_plex_acc', methods=['POST'])
def authenticate_plex_acc():
global globalPin
globalPin = MyPlexPinLogin(oauth=True)
oauth_url = globalPin.oauthUrl()
return jsonify({'oauth_url': oauth_url})

@app.route('/fetch_libraries/<serverName>')
def fetch_libraries(serverName):
Expand Down Expand Up @@ -324,4 +323,4 @@ def get_recommendated_movies():
moviesFromSelectedLibrary = {}

if __name__ == '__main__':
app.run(debug=False)
app.run(host='0.0.0.0', port=5000, debug=False)
11 changes: 11 additions & 0 deletions app.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[uwsgi]
module = wsgi:app
http = :5000
master = true
processes = 5

socket = app.sock
chmod-socket = 660
vacuum = true

die-on-term = true
1 change: 0 additions & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# Define the base URLs
TMDB_BASE_URL = "https://api.themoviedb.org/3"
TMDB_IMAGE_BASE_URL = "https://image.tmdb.org/t/p/w500"
PLEX_CLIENT_IDENTIFIER = "your_unique_client_identifier"

# Define the response messages
RESPONSE_MESSAGES = {
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Flask
requests
plexapi
plexapi
uwsgi
6 changes: 3 additions & 3 deletions templates/about.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

<head>
<title>Gaps</title>
<title>Gaps 2</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link href="{{ url_for('static', filename='images/gaps.ico') }}" rel="shortcut icon"/>
<link href="{{ url_for('static', filename='css/libraries/bootstrap.min.css') }}" rel="stylesheet"/>
Expand All @@ -22,10 +22,10 @@
<h3 class="top-margin">About</h3>

<p class="text-muted">
GAPS 2 is a rewrite of the original <a href="https://github.com/JasonHHouse/gaps" target="_blank">GAPS project</a>, now written in Python instead of Java. GAPS (Gaps A Plex Server) finds movies you're missing in your Plex Server. It's a great way to find additional movies that you might be interested in based on collections from movies in your Plex Server.
<a href="https://github.com/primetime43/GAPS-2" target="_blank">GAPS 2</a> is a rewrite of the original <a href="https://github.com/JasonHHouse/gaps" target="_blank">GAPS project</a>, now written in Python instead of Java. GAPS (Gaps A Plex Server) finds movies you're missing in your Plex Server. It's a great way to find additional movies that you might be interested in based on collections from movies in your Plex Server.
</p>
<p class="text-muted">
The GAPS 2 project aims to bring the same functionality with the simplicity and versatility of Python.
The <a href="https://github.com/primetime43/GAPS-2" target="_blank">GAPS 2</a> project aims to bring the same functionality with the simplicity and versatility of Python.
</p>

<p class="text-muted">Gaps searches through your Plex Server. It then queries
Expand Down
156 changes: 126 additions & 30 deletions templates/configuration.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@ <h3 class="top-margin">Settings</h3>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#plex" id="plexTab">Plex</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#jellyfin" id="jellyfinTab">Jellyfin</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#emby" id="embyTab">Emby</a>
</li>
</ul>
<div class="tab-content top-margin" id="myTabContent">
<!-- TMdb tab -->
<div class="tab-pane fade show active top-margin" id="tmdb">
<p>To use Gaps, you'll need a MovieDB api key. Navigate over to <a
href="https://www.themoviedb.org/settings/api" rel="noopener noreferrer" target="_blank">The
Expand Down Expand Up @@ -148,13 +155,18 @@ <h3 class="top-margin">Settings</h3>

</form>
</div>
<!-- Plex tab -->
<div class="tab-pane fade top-margin" id="plex">
<form class="needs-validation" id="plexConfiguration" novalidate>

<div class="form-group mt-3">
<button class="btn btn-primary" type="button" id="linkPlexAccountBtn">Link Plex Account</button>
</div>

<div>
<button class="btn btn-primary" type="button" id="authPlexAccountBtn">Authenticate Plex Account</button>
</div>
<div class="mt-3">
<button id="fetchServersBtn" class="btn btn-primary" type="button">Fetch Servers</button>
</div>
</div>

<div class="form-group">
<label for="server">Plex Server</label>
Expand Down Expand Up @@ -191,6 +203,41 @@ <h3 class="top-margin">Servers</h3>
<div id="activeServerInfo"></div>
</form>
</div>
<!-- Jellyfin tab -->
<div class="tab-pane fade top-margin" id="jellyfin">
<form class="needs-validation" id="jellyfinConfiguration" novalidate>
<div class="form-group">
<label for="jellyfinServer">Jellyfin Server Address</label>
<input class="form-control" id="jellyfinServer" required type="text">
<div class="invalid-feedback">
Please enter your Jellyfin server address.
</div>
</div>
<div class="form-group">
<label for="jellyfinUsername">Jellyfin Username</label>
<input class="form-control" id="jellyfinUsername" required type="text">
<div class="invalid-feedback">
Please enter your Jellyfin username.
</div>
</div>
<div class="form-group">
<label for="jellyfinPassword">Jellyfin Password</label>
<div class="input-group">
<input class="form-control" id="jellyfinPassword" required type="password">
<div class="input-group-append">
<button id="toggleJellyfinPasswordVisibility" class="btn btn-outline-secondary" type="button">Show</button>
</div>
</div>
<div class="invalid-feedback">
Please enter your Jellyfin password.
</div>
</div>
<button class="btn btn-primary" type="submit">Save</button>
</form>
</div>
<!-- Emby tab -->
<div class="tab-pane fade top-margin" id="emby">
</div>
</div>

<div th:insert="fragments/common :: contextPath"></div>
Expand Down Expand Up @@ -222,37 +269,19 @@ <h4 class="alert-heading">Error!</h4>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<!-- Authenticates the user's account -->
<script>
$(document).ready(function () {
$('#linkPlexAccountBtn').on('click', function () {
// Show the loading text/spinner
$('#loading').show();

$.post('/link_plex_account', function (response) {
// Check if there's a message in the response
if (response.message) {
// Show an alert with the message
alert(response.message);
} else {
// Update the dropdown options with the server data
var servers = response.servers;
var dropdown = $('#server');
dropdown.empty();
servers.forEach(function (server) {
dropdown.append($('<option>').text(server).attr('value', server));
});

// Set the Plex token
var token = response.token;
$('#plexToken').val(token);
}

// Hide the loading text/spinner
$('#loading').hide();
$('#authPlexAccountBtn').on('click', function () {
// Send a POST request to the /authenticate_plex_acc endpoint
$.post('/authenticate_plex_acc', function (response) {
// Open the OAuth URL in a new tab
window.open(response.oauth_url, '_blank');
});
});
});
</script>
</script>

<script>
$('#togglePlexTokenVisibility').on('click', function () {
Expand All @@ -267,6 +296,33 @@ <h4 class="alert-heading">Error!</h4>
});
</script>

<!-- Fetches the servers after the user has been authenticated -->
<script>
$('#fetchServersBtn').on('click', function () {
// Show the loading text/spinner
$('#loading').show();
$.post('/fetch_servers', function (response) {
if (response.message) {
alert(response.message);
} else {
// Update the dropdown options with the server data
var servers = response.servers;
var dropdown = $('#server');
dropdown.empty();
servers.forEach(function (server) {
dropdown.append($('<option>').text(server).attr('value', server));
});

// Set the Plex token
var token = response.token;
$('#plexToken').val(token);
}
// Hide the loading text/spinner
$('#loading').hide();
});
});
</script>

<script>
function displayActiveServerInfo(activeServer, libraries) {
var activeServerInfo = document.getElementById('activeServerInfo');
Expand All @@ -293,6 +349,10 @@ <h4 class="alert-heading">Error!</h4>

// Function to be called on page load
window.addEventListener('load', function () {
// Hide the Jellyfin & emby tab
$('#jellyfinTab').hide();
$('#embyTab').hide();

// Send an AJAX request to get the active server data from the Python side
$.ajax({
url: '/get_active_server',
Expand Down Expand Up @@ -345,7 +405,7 @@ <h4 class="alert-heading">Error!</h4>

// Hide the loading text/spinner
$('#saving').hide();

showActiveServerData(event);
},
error: function (error) {
// Handle error response here
Expand Down Expand Up @@ -394,6 +454,42 @@ <h4 class="alert-heading">Error!</h4>

</script>

<!-- Jellyfin Scripts -->
<script>
$('#jellyfinConfiguration').on('submit', function (event) {
event.preventDefault();

var server = $('#jellyfinServer').val();
var username = $('#jellyfinUsername').val();
var password = $('#jellyfinPassword').val();

$.ajax({
url: '/save_jellyfin_data',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ server: server, username: username, password: password }),
success: function (response) {
console.log('Jellyfin data saved successfully');
},
error: function (error) {
console.log('Failed to save Jellyfin data');
}
});
});

// Show/hide the password button
$('#toggleJellyfinPasswordVisibility').on('click', function () {
var jellyfinPasswordInput = $('#jellyfinPassword');
if (jellyfinPasswordInput.attr('type') === 'password') {
jellyfinPasswordInput.attr('type', 'text');
$(this).text('Hide');
} else {
jellyfinPasswordInput.attr('type', 'password');
$(this).text('Show');
}
});
</script>

<script src="{{ url_for('static', filename='js/libraries/jquery-3.4.1.min.js') }}"
type="text/javascript"></script>
<script src="{{ url_for('static', filename='js/libraries/bootstrap.bundle.min.js') }}"
Expand Down
Loading

0 comments on commit de1753d

Please sign in to comment.