Skip to content

Commit

Permalink
Full working Plex login with webhook, server selection and library se…
Browse files Browse the repository at this point in the history
…lection. Implemented film/tvshow image as background
  • Loading branch information
giuseppe99barchetta committed Oct 23, 2024
1 parent 45b4559 commit ba0d742
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 102 deletions.
1 change: 1 addition & 0 deletions plex/plex_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __init__(self, token, api_url=None, max_content=10, library_ids=None, client
"X-Plex-Token": token,
"Accept": 'application/json'
}

if client_id:
self.headers['X-Plex-Client-Identifier'] = client_id

Expand Down
106 changes: 51 additions & 55 deletions suggestarr-frontend/src/api/PlexApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,22 @@ export default {
props: ['config'],
data() {
return {
plexTestState: {
status: null,
isTesting: false
},
loading: false,
loadingLibraries: false,
servers: [],
selectedServer: null,
selectedServerConnection: null, // Connessione selezionata
selectedServerConnection: null,
libraries: [],
selectedLibraries: [],
manualConfiguration: false, // Modalità di configurazione manuale
manualServerAddress: '', // Indirizzo del server per configurazione manuale
manualServerPort: '', // Porta del server per configurazione manuale
isLoggedIn: false // Stato del login
manualConfiguration: false,
manualServerAddress: '',
isLoggedIn: false
};
},
methods: {
toggleLibrarySelection(library) {
const index = this.selectedLibraries.findIndex(l => l.uuid === library.uuid);
if (index > -1) {
this.selectedLibraries.splice(index, 1);
} else {
this.selectedLibraries.push(library);
}

index > -1 ? this.selectedLibraries.splice(index, 1) : this.selectedLibraries.push(library);
this.updateSelectedLibraries();
},
isSelected(libraryId) {
Expand All @@ -39,83 +31,85 @@ export default {
},
loadSelectedLibraries() {
if (this.config.PLEX_LIBRARIES) {
console.log(this.config.PLEX_LIBRARIES)
this.selectedLibraries = this.libraries.filter(library =>
this.config.PLEX_LIBRARIES.includes(library.uuid)
);
}
},

// Funzione separata per eseguire le chiamate API
async apiRequest(url, method = 'get', data = null) {
try {
const response = await axios({ url, method, data, headers: this.config.headers });
return response;
} catch (error) {
console.error(`API Request error: ${error.message}`);
throw error;
}
},

async loginWithPlex() {
try {
// Chiedi al backend il pin e l'URL di login
const response = await axios.post('/api/plex/auth');
this.loading = true;
const response = await this.apiRequest('/api/plex/auth', 'post');
const { pin_id, auth_url } = response.data;

// Reindirizza l'utente alla pagina di login di Plex
window.open(auth_url, '_blank', 'width=800,height=600');

this.startPolling(pin_id);
} catch (error) {
console.error('Error during Plex login:', error);
this.$toast.error('Error during Plex login.');
}
},

async startPolling(pin_id) {
console.log("Starting polling for Pin ID:", pin_id);
const interval = setInterval(async () => {
try {
// Controlla se il login è completato e il token è disponibile
const response = await axios.get(`/api/plex/check-auth/${pin_id}`);
const response = await this.apiRequest(`/api/plex/check-auth/${pin_id}`);
const { auth_token } = response.data;

if (auth_token) {
// Se ottieni il token, interrompi il polling e salva il token
clearInterval(interval);
// Puoi salvare il token o inviare il token a una funzione di autenticazione
this.$emit('update-config', 'PLEX_TOKEN', auth_token);
this.fetchPlexServers(auth_token)
this.isLoggedIn = true
await this.fetchPlexServers(auth_token);
this.isLoggedIn = true;
}
} catch (error) {
console.error('Error checking Plex auth status:', error);
} finally {
this.loading = false;
}
}, 3000); // Polling ogni 3 secondi
}, 3000);
},

async fetchPlexServers(auth_token) {
try {
const response = await axios.post('/api/plex/servers', {
'auth_token': auth_token
});

const response = await this.apiRequest('/api/plex/servers', 'post', { auth_token });
if (response.status === 200 && response.data.servers) {
this.servers = response.data.servers;
console.log('Plex Servers:', this.servers);

if (this.servers.length > 0) {
this.selectedServer = this.servers[0];
}
} else {
console.error('Failed to fetch servers:', response.data.message);
this.$toast.error('Failed to fetch servers.');
}
} catch (error) {
console.error('Error fetching Plex servers:', error);
this.$toast.error('Error fetching Plex servers.');
}
},
updateSelectedServer() {
this.libraries = [] // reset previously loaded library if new Plex server was selected

updateSelectedServer() {
this.libraries = []; // Reset libraries if a new server is selected
if (this.selectedServerConnection === 'manual') {
this.manualConfiguration = true;
} else {
this.manualConfiguration = false;
// Usa la connessione selezionata per aggiornare la configurazione
const { address, port, protocol } = this.selectedServerConnection;
this.$emit('update-config', 'PLEX_API_URL', protocol + '://' + address + ':' + port);
this.$emit('update-config', 'PLEX_API_URL', `${protocol}://${address}:${port}`);
}
},

getServerConnections() {
// Funzione che raccoglie tutte le connessioni dai server e le prepara per il dropdown
const connections = [];
this.servers.forEach(server => {
return this.servers.reduce((connections, server) => {
server.connections.forEach(connection => {
connections.push({
serverName: server.name,
Expand All @@ -125,36 +119,38 @@ export default {
secure: connection.protocol === 'https'
});
});
});
return connections;
return connections;
}, []);
},

async fetchLibraries() {
this.loadingLibraries = true;
try {
let tost_loading_libs = this.$toast.info('Fetching Plex libraries..');
const response = await axios.post('/api/plex/libraries', {
let tost_loading_libs = this.$toast.info('Fetching Plex libraries...');
const response = await this.apiRequest('/api/plex/libraries', 'post', {
PLEX_API_URL: this.config.PLEX_API_URL,
PLEX_TOKEN: this.config.PLEX_TOKEN
});

if (response.status === 200 && response.data.items) {
this.libraries = response.data.items;
} else {
this.$toast.error('Failed to fetch libraries:', response.data.message);
this.$toast.error('Failed to fetch libraries.');
}

tost_loading_libs.dismiss()

tost_loading_libs.dismiss();
} catch (error) {
this.$toast.error('Failed to fetch libraries:', error);
this.$toast.error('Error fetching libraries.');
} finally {
this.loadingLibraries = false;
}
},
},
mounted() {
// Controlla se c'è già un auth_token salvato
const authToken = this.config.PLEX_TOKEN;
if (authToken) {
this.isLoggedIn = true;
this.fetchPlexServers(authToken);
}
},
};
}
};
21 changes: 1 addition & 20 deletions suggestarr-frontend/src/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,4 @@ export function fetchJellyfinLibraries(apiUrl, apiKey) {
JELLYFIN_API_URL: apiUrl,
JELLYFIN_TOKEN: apiKey
});
}

// Function to fetch Plex libraries
export async function getPlexLibraries(plexUrl, plexToken) {
const apiUrl = `/api/plex/libraries`;
try {
const response = await axios.post(apiUrl, {
PLEX_API_URL: plexUrl,
PLEX_TOKEN: plexToken,
});

if (response.status === 200) {
return response.data;
}
} catch (error) {
console.error('Error fetching Plex libraries:', error);
}

return { items: [] };
}
}
23 changes: 20 additions & 3 deletions suggestarr-frontend/src/assets/styles/wizard.css
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
.wizard-container {
background-color: #1a202c;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #1a202c;
/* Dark background */
background-size: cover;
background-position: center;
background-repeat: no-repeat;
transition: background-image 0.7s ease-in-out;
/* Dark overlay for better text contrast */
position: relative;
z-index: 1;
}

.wizard-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: -1;
}

.wizard-content {
padding: 20px;
background-color: #2d3748;
background-color: #2d3748e7 !important;
border-radius: 10px;
max-width: 800px;
width: 100%;
Expand Down
69 changes: 56 additions & 13 deletions suggestarr-frontend/src/components/ConfigWizard.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div>
<div v-if="currentStep <= steps.length" class="wizard-container">
<div v-if="currentStep <= steps.length" class="wizard-container"
:style="{ backgroundImage: 'url(' + backgroundImageUrl + ')' }">
<div class="wizard-content">
<h2 class="text-3xl font-bold text-gray-200 mb-6 text-center">SuggestArr Wizard</h2>
<div class="progress-bar">
Expand All @@ -10,10 +11,8 @@

<!-- Use dynamic components for each step -->
<transition name="fade" mode="out-in">
<component :is="currentStepComponent" :config="config"
@next-step="handleStepChange(1)"
@previous-step="handleStepChange(-1)"
@update-config="updateConfig" />
<component :is="currentStepComponent" :config="config" @next-step="handleStepChange(1)"
@previous-step="handleStepChange(-1)" @update-config="updateConfig" />
</transition>
<Footer />
</div>
Expand Down Expand Up @@ -54,6 +53,8 @@ export default {
return {
currentStep: 1, // Current step of the wizard
config: this.getInitialConfig(), // Load initial configuration
backgroundImageUrl: '',
intervalId: null,
};
},
computed: {
Expand All @@ -76,11 +77,7 @@ export default {
mounted() {
// Fetch the saved configuration when component mounts
this.fetchConfig();
if (this.$toast) {
console.log('Toast is available');
} else {
console.error('Toast is not available in this component');
}
this.startBackgroundImageRotation();
},
methods: {
// Initialize the configuration with default values
Expand All @@ -93,8 +90,8 @@ export default {
SEER_TOKEN: '', // Unified for Jellyseer/Overseer
SEER_USER_NAME: '', // Unified for Jellyseer/Overseer
SEER_USER_PSW: '', // Unified for Jellyseer/Overseer
MAX_SIMILAR_MOVIE: 5,
MAX_SIMILAR_TV: 2,
MAX_SIMILAR_MOVIE: 5,
MAX_SIMILAR_TV: 2,
MAX_CONTENT_CHECKS: 10,
CRON_TIMES: '0 0 * * *',
JELLYFIN_LIBRARIES: [],
Expand Down Expand Up @@ -148,6 +145,52 @@ export default {
editConfig() {
this.currentStep = 1;
},
},
async fetchRandomMovieImage() {
const apiKey = this.config.TMDB_API_KEY;
if (!apiKey){
return
}
const randomPage = Math.floor(Math.random() * 100) + 1;
try {
const response = await axios.get(`https://api.themoviedb.org/3/movie/popular`, {
params: {
api_key: apiKey,
page: randomPage
}
});
const movies = response.data.results;
const randomMovie = movies[Math.floor(Math.random() * movies.length)];
const imageUrl = `https://image.tmdb.org/t/p/w1280${randomMovie.backdrop_path}`;
// Pre-caricamento dell'immagine
const img = new Image();
img.src = imageUrl;
img.onload = () => {
this.backgroundImageUrl = imageUrl; // Cambia lo sfondo solo dopo che l'immagine è stata caricata
};
} catch (error) {
console.error('Failed to fetch movie image:', error);
}
},
startBackgroundImageRotation() {
this.fetchRandomMovieImage();
this.intervalId = setInterval(() => {
this.fetchRandomMovieImage();
}, 10000);
},
stopBackgroundImageRotation() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
}
}, beforeUnmount() {
this.stopBackgroundImageRotation();
}
};
</script>
Loading

0 comments on commit ba0d742

Please sign in to comment.