Skip to content

Commit 7cd543d

Browse files
authored
Merge pull request #643 from arabcoders/dev
Introduced a new page for validating Plex Tokens
2 parents 1446ce7 + c15050e commit 7cd543d

11 files changed

Lines changed: 486 additions & 69 deletions

File tree

frontend/components/Connection.vue

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<template>
2-
<div class="columns is-multiline">
3-
<div class="column is-12 mt-2">
2+
<div class="columns is-multiline mb-2">
3+
4+
<div class="column is-6-tablet">
45
<div class="card">
56
<header class="card-header">
67
<p class="card-header-title">
@@ -11,7 +12,7 @@
1112
</span>
1213
</header>
1314
<div class="card-content">
14-
<form @submit.prevent="testApi">
15+
<form id="api_connection" @submit.prevent="testApi">
1516
<div class="field">
1617
<label class="label" for="api_token">
1718
<span class="icon-text">
@@ -37,8 +38,7 @@
3738
<p class="help">
3839
You can obtain the <code>API TOKEN</code> by using the <code>system:apikey</code> command or by
3940
viewing the <code>/config/.env</code> inside <code>WS_DATA_PATH</code> variable and looking for
40-
the
41-
<code>WS_API_KEY=</code> key.
41+
the <code>WS_API_KEY=</code> key.
4242
</p>
4343
</div>
4444
</div>
@@ -83,50 +83,82 @@
8383
</div>
8484
</div>
8585
</div>
86+
</form>
87+
</div>
88+
</div>
89+
</div>
8690

87-
<div class="field">
88-
<label class="label" for="random_bg">Backgrounds</label>
89-
<div class="control">
90-
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
91-
<label for="random_bg">Enable</label>
92-
<p class="help">Use random background image from your media backends.</p>
93-
</div>
91+
<div class="column is-6-tablet">
92+
<div class="card">
93+
<header class="card-header">
94+
<p class="card-header-title">
95+
WebUI Look & Feel
96+
</p>
97+
<span class="card-header-icon">
98+
<span class="icon"><i class="fas fa-paint-brush"/></span>
99+
</span>
100+
</header>
101+
<div class="card-content">
102+
<div class="field">
103+
<label class="label" for="random_bg">Color scheme</label>
104+
<div class="control">
105+
<label for="auto" class="radio">
106+
<input id="auto" type="radio" v-model="webui_theme" value="auto"> System Default
107+
</label>
108+
<label for="light" class="radio">
109+
<input id="light" type="radio" v-model="webui_theme" value="light"> Light
110+
</label>
111+
<label for="dark" class="radio">
112+
<input id="dark" type="radio" v-model="webui_theme" value="dark"> Dark
113+
</label>
94114
</div>
115+
<p class="help">
116+
<span class="icon"><i class="fa-solid fa-info"/></span>
117+
<span>Select the color scheme for the WebUI.</span>
118+
</p>
119+
</div>
95120

96-
<div class="field">
97-
<label class="label" for="random_bg_opacity">
98-
Background Visibility: (<code>{{ bg_opacity }}</code>)
99-
</label>
100-
<div class="control">
101-
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.60"
102-
max="1.00" step="0.05">
103-
<p class="help">How visible the background image should be.</p>
104-
</div>
121+
<div class="field">
122+
<label class="label" for="random_bg">Backgrounds</label>
123+
<div class="control">
124+
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
125+
<label for="random_bg">Enable</label>
126+
<p class="help">Use random background image from your media backends.</p>
105127
</div>
128+
</div>
106129

107-
<div class="field has-text-right">
108-
<div class="control">
109-
<button type="submit" class="button is-primary" :disabled="!api_url || !api_token">
110-
<span class="icon-text">
111-
<span class="icon"><i class="fas fa-save"/></span>
112-
<span>Save</span>
113-
</span>
114-
</button>
115-
</div>
116-
<p class="help has-text-left">
117-
<span class="icon-text">
118-
<span class="icon has-text-danger"><i class="fas fa-info"/></span>
119-
<span>These settings are stored locally in your browser. You need to re-add them if you access
120-
the
121-
<code>WebUI</code> from different browser.</span>
122-
</span>
123-
</p>
130+
<div class="field">
131+
<label class="label" for="random_bg_opacity">
132+
Background Visibility: (<code>{{ bg_opacity }}</code>)
133+
</label>
134+
<div class="control">
135+
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.60"
136+
max="1.00" step="0.05">
137+
<p class="help">How visible the background image should be.</p>
124138
</div>
125-
</form>
139+
</div>
126140
</div>
127141
</div>
128142
</div>
129143

144+
<div class="column is-12">
145+
<div class="control">
146+
<button form="api_connection" type="submit" class="button is-primary is-fullwidth"
147+
:disabled="!api_url || !api_token">
148+
<span class="icon-text">
149+
<span class="icon"><i class="fas fa-save"/></span>
150+
<span>Save</span>
151+
</span>
152+
</button>
153+
</div>
154+
<p class="has-text-left">
155+
<span class="icon has-text-danger"><i class="fas fa-info"/></span>
156+
<span>These settings are stored locally in your browser. You need to re-add them if you access the
157+
<code>WebUI</code> from different browser.
158+
</span>
159+
</p>
160+
</div>
161+
130162
<div class="column is-12 mt-2">
131163
<Message title="Information" message_class="has-background-info-90 has-text-dark" icon="fas fa-info-circle">
132164
<p>
@@ -168,6 +200,8 @@ const real_api_url = useStorage('api_url', window.location.origin)
168200
const real_api_path = useStorage('api_path', '/v1/api')
169201
const real_api_token = useStorage('api_token', '')
170202
203+
const webui_theme = useStorage('theme', 'auto')
204+
171205
172206
const api_url = ref(toRaw(real_api_url.value))
173207
const api_path = ref(toRaw(real_api_path.value))

frontend/layouts/default.vue

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -138,22 +138,7 @@
138138
</div>
139139
</div>
140140
<div class="navbar-end pr-3">
141-
<div class="navbar-item">
142-
<button class="button is-dark has-tooltip-bottom" v-tooltip.bottom="'Switch to Light theme'"
143-
v-if="'auto' === selectedTheme" @click="selectTheme('light')">
144-
<span class="icon has-text-warning"><i class="fas fa-sun"/></span>
145-
</button>
146-
<button class="button is-dark has-tooltip-bottom" v-tooltip.bottom="'Switch to Dark theme'"
147-
v-if="'light' === selectedTheme" @click="selectTheme('dark')">
148-
<span class="icon"><i class="fas fa-moon"/></span>
149-
</button>
150-
<button class="button is-dark has-tooltip-bottom" v-tooltip.bottom="'Switch to Auto theme'"
151-
v-if="'dark' === selectedTheme" @click="selectTheme('auto')">
152-
<span class="icon"><i class="fas fa-microchip"/></span>
153-
</button>
154-
</div>
155-
156-
<div class="navbar-item" v-if="hasAPISettings">
141+
<div class="navbar-item" v-if="hasAPISettings && !showConnection">
157142
<button class="button is-dark" @click="showUserSelection = !showUserSelection" v-tooltip="'Change User'">
158143
<span class="icon"><i class="fas fa-users"/></span>
159144
</button>
@@ -223,7 +208,7 @@ import Markdown from '~/components/Markdown'
223208
import UserSelection from '~/components/UserSelection'
224209
import Connection from '~/components/Connection'
225210
226-
const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')())
211+
const selectedTheme = useStorage('theme', 'auto')
227212
const showUserSelection = ref(false)
228213
const showConnection = ref(false)
229214
@@ -306,6 +291,10 @@ onMounted(async () => {
306291
307292
watch(selectedTheme, value => {
308293
try {
294+
if ('auto' === value) {
295+
applyPreferredColorScheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
296+
return
297+
}
309298
applyPreferredColorScheme(value)
310299
} catch (e) {
311300
}
@@ -349,13 +338,6 @@ const changeRoute = async (_, callback) => {
349338
}
350339
}
351340
352-
const selectTheme = theme => {
353-
selectedTheme.value = theme
354-
if ('auto' === theme) {
355-
return window.location.reload()
356-
}
357-
}
358-
359341
const handleConnection = data => {
360342
api_version.value = data.version
361343
showConnection.value = false

frontend/pages/backends/index.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@
5353
</Message>
5454
</div>
5555

56+
<div class="column is-12">
57+
<div class="content">
58+
<h1 class="title is-4">Tools</h1>
59+
<ul>
60+
<li>
61+
<NuxtLink :to="`/plex_token`" v-text="'Validate plex token'"/>
62+
</li>
63+
</ul>
64+
</div>
65+
</div>
66+
5667
<div v-for="backend in backends" :key="backend.name" class="column is-6-tablet is-12-mobile">
5768
<div class="card">
5869
<header class="card-header">
@@ -162,6 +173,7 @@
162173
</footer>
163174
</div>
164175
</div>
176+
165177
<div class="column is-12">
166178
<Message message_class="has-background-info-90 has-text-dark" :toggle="show_page_tips"
167179
@toggle="show_page_tips = !show_page_tips" :use-toggle="true" title="Tips" icon="fas fa-info-circle">

frontend/pages/plex_token.vue

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<template>
2+
<div>
3+
<div class="columns is-multiline">
4+
<div class="column is-12 is-clearfix is-unselectable">
5+
<span class="title is-4">
6+
<span class="icon"><i class="fas fa-server"/></span>
7+
Validate Plex Token
8+
</span>
9+
<div class="is-pulled-right"></div>
10+
<div class="is-hidden-mobile">
11+
You can use this page to validate if given token is able to communicates with the plex.tv API.
12+
</div>
13+
</div>
14+
15+
<div class="column is-12">
16+
<form @submit.prevent="validateToken">
17+
<div class="card">
18+
<div class="card-header">
19+
<p class="card-header-title">X-Plex-Token</p>
20+
</div>
21+
22+
<div class="card-content">
23+
<Message v-if="success" message_class="has-background-success-90 has-text-dark" title="Success!"
24+
icon="fas fa-check-circle" :useClose="true" @close="() => success = ''">
25+
<p>
26+
<span class="icon"><i class="fas fa-check"/></span>
27+
<span>{{ success }}</span>
28+
</p>
29+
</Message>
30+
<Message v-if="error" message_class="has-background-danger-90 has-text-dark"
31+
title="Error" icon="fas fa-exclamation-triangle" :useClose="true"
32+
@close="() => error = ''">
33+
<p>
34+
<span class="icon"><i class="fas fa-exclamation"/></span>
35+
<span>{{ error }}</span>
36+
</p>
37+
</Message>
38+
39+
<div class="field">
40+
<div class="field has-addons">
41+
<div class="control is-expanded has-icons-left">
42+
<input class="input" v-model="token" required placeholder="X-Plex-Token"
43+
:type="false === exposeToken ? 'password' : 'text'">
44+
<span class="icon is-left"><i class="fas fa-key"/></span>
45+
</div>
46+
<div class="control">
47+
<button type="button" class="button is-primary" @click="exposeToken = !exposeToken"
48+
v-tooltip="'Show/Hide token'">
49+
<span class="icon" v-if="!exposeToken"><i class="fas fa-eye"/></span>
50+
<span class="icon" v-else><i class="fas fa-eye-slash"/></span>
51+
</button>
52+
</div>
53+
</div>
54+
<p>
55+
Enter the <code>X-Plex-Token</code>.
56+
<NuxtLink target="_blank" to="https://support.plex.tv/articles/204059436"
57+
v-text="'Visit This link'"/>
58+
to learn how to get the token.
59+
</p>
60+
</div>
61+
</div>
62+
<div class="card-footer">
63+
<div class="card-footer-item">
64+
<button class="button is-fullwidth is-primary" type="submit" :disabled="!token || isLoading">
65+
<template v-if="isLoading">
66+
<span class="icon"><i class="fas fa-spinner fa-spin"></i></span>
67+
<span>Validating...</span>
68+
</template>
69+
<template v-else>
70+
<span class="icon"><i class="fas fa-check"></i></span>
71+
<span>Validate</span>
72+
</template>
73+
</button>
74+
</div>
75+
</div>
76+
</div>
77+
</form>
78+
</div>
79+
</div>
80+
</div>
81+
</template>
82+
83+
<script setup>
84+
85+
import request from "~/utils/request.js";
86+
import {parse_api_response} from "~/utils/index.js";
87+
88+
const isLoading = ref(false)
89+
const token = ref('')
90+
const error = ref('')
91+
const success = ref('')
92+
const exposeToken = ref(false)
93+
94+
const validateToken = async () => {
95+
error.value = ''
96+
success.value = ''
97+
98+
if (!token.value) {
99+
error.value = 'Please enter a valid token.'
100+
return
101+
}
102+
103+
try {
104+
105+
isLoading.value = true
106+
107+
const response = await request(`/backends/validate/token/plex`, {
108+
method: 'POST',
109+
body: JSON.stringify({token: token.value})
110+
})
111+
112+
const resp = await parse_api_response(response)
113+
114+
if (200 !== response.status) {
115+
error.value = resp.error.message
116+
return
117+
}
118+
119+
success.value = resp.info.message
120+
} catch (e) {
121+
error.value = `An error occurred while validating the token. ${e.message}`
122+
} finally {
123+
isLoading.value = false
124+
}
125+
}
126+
127+
</script>

0 commit comments

Comments
 (0)