mirror of
https://github.com/KevinMidboe/seasoned.git
synced 2026-04-24 16:53:37 +00:00
Minor improvements and code cleanup across components
- Refactor PlexSettings component for better organization - Add utility functions to utils.ts for shared logic - Update API methods for improved error handling - Clean up navigation icon components - Remove unused code from CommandPalette and SeasonedButton - Fix minor issues in Movie popup component - Update page component imports (RegisterPage, TorrentsPage)
This commit is contained in:
16
src/api.ts
16
src/api.ts
@@ -413,6 +413,20 @@ const unlinkPlexAccount = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const plexRecentlyAddedInLibrary = async (id: number) => {
|
||||||
|
const url = new URL(`/api/v2/plex/recently_added/${id}`, API_HOSTNAME);
|
||||||
|
const options: RequestInit = {
|
||||||
|
credentials: "include"
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetch(url.href, options)
|
||||||
|
.then(resp => resp.json())
|
||||||
|
.catch(error => {
|
||||||
|
console.error(`api error fetch plex recently added`); // eslint-disable-line no-console
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// - - - User graphs - - -
|
// - - - User graphs - - -
|
||||||
|
|
||||||
const fetchGraphData = async (
|
const fetchGraphData = async (
|
||||||
@@ -543,6 +557,7 @@ const elasticSearchMoviesAndShows = async (query: string, count = 22) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
API_HOSTNAME,
|
||||||
getMovie,
|
getMovie,
|
||||||
getShow,
|
getShow,
|
||||||
getPerson,
|
getPerson,
|
||||||
@@ -559,6 +574,7 @@ export {
|
|||||||
getRequestStatus,
|
getRequestStatus,
|
||||||
linkPlexAccount,
|
linkPlexAccount,
|
||||||
unlinkPlexAccount,
|
unlinkPlexAccount,
|
||||||
|
plexRecentlyAddedInLibrary,
|
||||||
register,
|
register,
|
||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
|
|||||||
@@ -42,8 +42,7 @@
|
|||||||
.navigation-link {
|
.navigation-link {
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
height: var(--header-size);
|
min-height: var(--header-size);
|
||||||
width: var(--header-size);
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 1rem 0.15rem;
|
padding: 1rem 0.15rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -90,18 +90,10 @@
|
|||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
grid-template-rows: var(--header-size);
|
grid-template-rows: var(--header-size);
|
||||||
grid-auto-flow: row;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include mobile {
|
@include mobile {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.navigation-icons > *:last-child) {
|
|
||||||
margin-top: auto;
|
|
||||||
justify-self: end;
|
|
||||||
align-self: end;
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -215,7 +215,8 @@
|
|||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
const ASSET_URL = "https://image.tmdb.org/t/p/";
|
const ASSET_URL = "https://image.tmdb.org/t/p/";
|
||||||
const COLORS_URL = "https://colors.schleppe.cloud/colors";
|
// const COLORS_URL = "https://colors.schleppe.cloud/colors";
|
||||||
|
const COLORS_URL = "http://localhost:8080/colors";
|
||||||
const ASSET_SIZES = ["w500", "w780", "original"];
|
const ASSET_SIZES = ["w500", "w780", "original"];
|
||||||
|
|
||||||
const media: Ref<IMovie | IShow> = ref();
|
const media: Ref<IMovie | IShow> = ref();
|
||||||
@@ -435,7 +436,7 @@
|
|||||||
|
|
||||||
> img {
|
> img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: inherit;
|
border-radius: calc(1.6rem - 1px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="plex-settings">
|
<div class="plex-settings">
|
||||||
<!-- Unconnected state -->
|
<!-- Unconnected state -->
|
||||||
<PlexAuthButton
|
<PlexAuthButton
|
||||||
v-if="!isPlexConnected"
|
v-if="!showPlexInformation"
|
||||||
@auth-success="handleAuthSuccess"
|
@auth-success="handleAuthSuccess"
|
||||||
@auth-error="handleAuthError"
|
@auth-error="handleAuthError"
|
||||||
/>
|
/>
|
||||||
@@ -16,20 +16,20 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<PlexLibraryStats
|
<PlexLibraryStats
|
||||||
:movies="libraryStats.movies"
|
:movies="libraryStats?.movies"
|
||||||
:shows="libraryStats.shows"
|
:shows="libraryStats?.['tv shows']"
|
||||||
:music="libraryStats.music"
|
:music="libraryStats?.music"
|
||||||
:watchtime="libraryStats.watchtime"
|
:watchtime="libraryStats?.watchtime || 0"
|
||||||
:loading="loadingLibraries"
|
:loading="syncingLibrary"
|
||||||
@open-library="showLibraryDetails"
|
@open-library="showLibraryDetails"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PlexServerInfo
|
<PlexServerInfo
|
||||||
:serverName="plexServer"
|
:serverName="plexServer"
|
||||||
:lastSync="lastSync"
|
:lastSync="lastSync"
|
||||||
:syncing="syncing"
|
:syncing="syncingServer"
|
||||||
@sync="syncLibrary"
|
@sync="syncLibrary"
|
||||||
@unlink="confirmUnlink"
|
@unlink="() => (showUnlinkModal = true)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -38,16 +38,18 @@
|
|||||||
|
|
||||||
<!-- Unlink Confirmation Modal -->
|
<!-- Unlink Confirmation Modal -->
|
||||||
<PlexUnlinkModal
|
<PlexUnlinkModal
|
||||||
v-if="showConfirmModal"
|
v-if="showUnlinkModal"
|
||||||
@confirm="unauthenticatePlex"
|
@confirm="unauthenticatePlex"
|
||||||
@cancel="cancelUnlink"
|
@cancel="() => (showUnlinkModal = false)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Library Details Modal -->
|
<!-- Library Details Modal -->
|
||||||
<PlexLibraryModal
|
<PlexLibraryModal
|
||||||
v-if="showLibraryModal && selectedLibrary"
|
v-if="showLibraryModal && selectedLibrary"
|
||||||
:libraryType="selectedLibrary"
|
:libraryType="selectedLibrary"
|
||||||
:details="libraryDetails[selectedLibrary]"
|
:details="libraryStats[selectedLibrary]"
|
||||||
|
:serverUrl="plexServerUrl"
|
||||||
|
:serverMachineId="plexMachineId"
|
||||||
@close="closeLibraryModal"
|
@close="closeLibraryModal"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +57,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
import { useStore } from "vuex";
|
|
||||||
import SeasonedMessages from "@/components/ui/SeasonedMessages.vue";
|
import SeasonedMessages from "@/components/ui/SeasonedMessages.vue";
|
||||||
import PlexAuthButton from "@/components/plex/PlexAuthButton.vue";
|
import PlexAuthButton from "@/components/plex/PlexAuthButton.vue";
|
||||||
import PlexProfileCard from "@/components/plex/PlexProfileCard.vue";
|
import PlexProfileCard from "@/components/plex/PlexProfileCard.vue";
|
||||||
@@ -64,184 +65,167 @@
|
|||||||
import PlexUnlinkModal from "@/components/plex/PlexUnlinkModal.vue";
|
import PlexUnlinkModal from "@/components/plex/PlexUnlinkModal.vue";
|
||||||
import PlexLibraryModal from "@/components/plex/PlexLibraryModal.vue";
|
import PlexLibraryModal from "@/components/plex/PlexLibraryModal.vue";
|
||||||
import { usePlexAuth } from "@/composables/usePlexAuth";
|
import { usePlexAuth } from "@/composables/usePlexAuth";
|
||||||
import { usePlexApi } from "@/composables/usePlexApi";
|
import {
|
||||||
import { usePlexLibraries } from "@/composables/usePlexLibraries";
|
fetchPlexServers,
|
||||||
|
fetchPlexUserData,
|
||||||
|
fetchLibraryDetails
|
||||||
|
} from "@/composables/usePlexApi";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import { linkPlexAccount, unlinkPlexAccount } from "../../api";
|
|
||||||
import { ErrorMessageTypes } from "../../interfaces/IErrorMessage";
|
import { ErrorMessageTypes } from "../../interfaces/IErrorMessage";
|
||||||
import type { IErrorMessage } from "../../interfaces/IErrorMessage";
|
import type { IErrorMessage } from "../../interfaces/IErrorMessage";
|
||||||
|
|
||||||
const messages: Ref<IErrorMessage[]> = ref([]);
|
const messages: Ref<IErrorMessage[]> = ref([]);
|
||||||
const loading = ref(false);
|
const syncingServer = ref(false);
|
||||||
const syncing = ref(false);
|
const syncingLibrary = ref(false);
|
||||||
const showConfirmModal = ref(false);
|
const showUnlinkModal = ref(false);
|
||||||
const plexUsername = ref<string>("");
|
const plexUsername = ref<string>("");
|
||||||
const plexUserData = ref<any>(null);
|
const plexUserData = ref<any>(null);
|
||||||
const isPlexConnected = ref<boolean>(false);
|
const showPlexInformation = ref<boolean>(false);
|
||||||
const hasLocalStorageData = ref<boolean>(false);
|
const hasLocalStorageData = ref<boolean>(false);
|
||||||
const hasCookieData = ref<boolean>(false);
|
|
||||||
const showLibraryModal = ref<boolean>(false);
|
const showLibraryModal = ref<boolean>(false);
|
||||||
const selectedLibrary = ref<string>("");
|
const selectedLibrary = ref<string>("");
|
||||||
const loadingLibraries = ref<boolean>(false);
|
|
||||||
|
|
||||||
const plexServer = ref("");
|
const plexServer = ref("");
|
||||||
const plexServerUrl = ref("");
|
const plexServerUrl = ref("");
|
||||||
const plexMachineId = ref("");
|
const plexMachineId = ref("");
|
||||||
const lastSync = ref("");
|
const lastSync = ref(sessionStorage.getItem("plex_library_last_sync"));
|
||||||
const libraryStats = ref({
|
const libraryStats = ref({
|
||||||
movies: 0,
|
movies: 0,
|
||||||
shows: 0,
|
shows: 0,
|
||||||
music: 0,
|
music: 0,
|
||||||
watchtime: 0
|
watchtime: 0
|
||||||
});
|
});
|
||||||
const libraryDetails = ref<any>({
|
|
||||||
movies: {
|
|
||||||
total: 0,
|
|
||||||
recentlyAdded: [],
|
|
||||||
genres: [],
|
|
||||||
totalDuration: "0 hours"
|
|
||||||
},
|
|
||||||
shows: {
|
|
||||||
total: 0,
|
|
||||||
recentlyAdded: [],
|
|
||||||
genres: [],
|
|
||||||
totalEpisodes: 0,
|
|
||||||
totalDuration: "0 hours"
|
|
||||||
},
|
|
||||||
music: {
|
|
||||||
total: 0,
|
|
||||||
recentlyAdded: [],
|
|
||||||
genres: [],
|
|
||||||
totalTracks: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const store = useStore();
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "reload"): void;
|
(e: "reload"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// Composables
|
// Composables
|
||||||
const { getCookie, setPlexAuthCookie, cleanup } = usePlexAuth();
|
const { getPlexAuthCookie, setPlexAuthCookie, cleanup } = usePlexAuth();
|
||||||
const {
|
|
||||||
fetchPlexUserData,
|
|
||||||
fetchPlexServers,
|
|
||||||
fetchLibrarySections,
|
|
||||||
fetchLibraryDetails
|
|
||||||
} = usePlexApi();
|
|
||||||
const { loadLibraries } = usePlexLibraries();
|
|
||||||
|
|
||||||
// ----- Connection check -----
|
// ----- Connection check -----
|
||||||
function checkPlexConnection() {
|
function checkPlexConnection() {
|
||||||
const cachedData = localStorage.getItem("plex_user_data");
|
const authToken = getPlexAuthCookie();
|
||||||
const authToken = getCookie("plex_auth_token");
|
showPlexInformation.value = !!authToken;
|
||||||
const storeHasPlexUserId = store.getters["user/plexUserId"];
|
return showPlexInformation.value;
|
||||||
hasLocalStorageData.value = !!cachedData;
|
|
||||||
hasCookieData.value = !!authToken;
|
|
||||||
isPlexConnected.value = !!(cachedData || authToken || storeHasPlexUserId);
|
|
||||||
return isPlexConnected.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Library loading -----
|
// ----- Library loading -----
|
||||||
async function fetchPlexLibraries(authToken: string) {
|
async function loadPlexServer() {
|
||||||
try {
|
// return cached value from sessionStorage if exists
|
||||||
loadingLibraries.value = true;
|
const cacheKey = "plex_server_data";
|
||||||
const server = await fetchPlexServers(authToken);
|
const cachedData = sessionStorage.getItem(cacheKey);
|
||||||
if (!server) {
|
if (cachedData) {
|
||||||
console.error("No Plex server found");
|
const server = JSON.parse(cachedData);
|
||||||
return;
|
plexServer.value = server?.name;
|
||||||
}
|
plexServerUrl.value = server?.url;
|
||||||
|
plexMachineId.value = server?.machineIdentifier;
|
||||||
plexServer.value = server.name;
|
return;
|
||||||
plexServerUrl.value = server.url;
|
|
||||||
plexMachineId.value = server.machineIdentifier;
|
|
||||||
lastSync.value = new Date().toLocaleString();
|
|
||||||
|
|
||||||
const sections = await fetchLibrarySections(authToken, server.url);
|
|
||||||
if (!sections || sections.length === 0) {
|
|
||||||
console.error("No library sections found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await loadLibraries(
|
|
||||||
sections,
|
|
||||||
authToken,
|
|
||||||
server.url,
|
|
||||||
server.machineIdentifier,
|
|
||||||
plexUsername.value,
|
|
||||||
fetchLibraryDetails
|
|
||||||
);
|
|
||||||
|
|
||||||
libraryStats.value = result.stats;
|
|
||||||
libraryDetails.value = result.details;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[PlexSettings] Error fetching Plex libraries:", error);
|
|
||||||
} finally {
|
|
||||||
loadingLibraries.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get token from cookie
|
||||||
|
const authToken = getPlexAuthCookie();
|
||||||
|
if (!authToken) return;
|
||||||
|
|
||||||
|
// make api call for data
|
||||||
|
syncingServer.value = true;
|
||||||
|
const server = await fetchPlexServers(authToken);
|
||||||
|
|
||||||
|
if (server) {
|
||||||
|
// set server name & id
|
||||||
|
plexServer.value = server?.name;
|
||||||
|
plexServerUrl.value = server?.url;
|
||||||
|
plexMachineId.value = server?.machineIdentifier;
|
||||||
|
// cache in sessionStorage
|
||||||
|
sessionStorage.setItem(cacheKey, JSON.stringify(server));
|
||||||
|
|
||||||
|
// set last-sync date
|
||||||
|
const now = new Date().toLocaleString();
|
||||||
|
lastSync.value = now;
|
||||||
|
sessionStorage.setItem("plex_library_last_sync", now);
|
||||||
|
} else {
|
||||||
|
console.log("unable to load plex server informmation");
|
||||||
|
}
|
||||||
|
|
||||||
|
syncingServer.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- User data loading -----
|
// ----- User data loading -----
|
||||||
async function loadPlexUserData() {
|
async function loadPlexUserData() {
|
||||||
checkPlexConnection();
|
// return cached value from sessionStorage if exists
|
||||||
const cachedData = localStorage.getItem("plex_user_data");
|
const cacheKey = "plex_user_data";
|
||||||
|
const cachedData = sessionStorage.getItem(cacheKey);
|
||||||
hasLocalStorageData.value = !!cachedData;
|
hasLocalStorageData.value = !!cachedData;
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
try {
|
plexUserData.value = JSON.parse(cachedData);
|
||||||
plexUserData.value = JSON.parse(cachedData);
|
plexUsername.value = plexUserData.value.username;
|
||||||
plexUsername.value = plexUserData.value.username;
|
return;
|
||||||
isPlexConnected.value = true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[PlexSettings] Error parsing cached Plex data:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const authToken = getCookie("plex_auth_token");
|
|
||||||
hasCookieData.value = !!authToken;
|
// get token from cookie
|
||||||
if (authToken) {
|
const authToken = getPlexAuthCookie();
|
||||||
const userData = await fetchPlexUserData(authToken);
|
if (!authToken) return;
|
||||||
if (userData) {
|
|
||||||
plexUserData.value = userData;
|
// make api call for data
|
||||||
plexUsername.value = userData.username;
|
const userData = await fetchPlexUserData(authToken);
|
||||||
isPlexConnected.value = true;
|
|
||||||
} else if (!cachedData) {
|
if (userData) {
|
||||||
isPlexConnected.value = false;
|
// set plex user data
|
||||||
}
|
plexUserData.value = userData;
|
||||||
if (isPlexConnected.value) {
|
plexUsername.value = userData?.username;
|
||||||
await fetchPlexLibraries(authToken);
|
|
||||||
}
|
// cache in sessionStorage
|
||||||
|
sessionStorage.setItem(cacheKey, JSON.stringify(userData));
|
||||||
} else {
|
} else {
|
||||||
isPlexConnected.value = false;
|
console.log("unable to load user data from plex");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- Load plex libary details -----
|
||||||
|
async function loadPlexLibraries() {
|
||||||
|
// return cached value from sessionStorage if exists
|
||||||
|
const cacheKey = "plex_library_data";
|
||||||
|
const cachedData = sessionStorage.getItem(cacheKey);
|
||||||
|
hasLocalStorageData.value = !!cachedData;
|
||||||
|
if (cachedData) {
|
||||||
|
libraryStats.value = JSON.parse(cachedData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get token from cookie
|
||||||
|
const authToken = getPlexAuthCookie();
|
||||||
|
if (!authToken) return;
|
||||||
|
|
||||||
|
// make api call for data
|
||||||
|
syncingLibrary.value = true;
|
||||||
|
const library = await fetchLibraryDetails();
|
||||||
|
|
||||||
|
if (library) {
|
||||||
|
libraryStats.value = library;
|
||||||
|
// cache in sessionStorage
|
||||||
|
sessionStorage.setItem(cacheKey, JSON.stringify(library));
|
||||||
|
} else {
|
||||||
|
console.log("unable to load plex library details");
|
||||||
|
}
|
||||||
|
|
||||||
|
syncingLibrary.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
// ----- OAuth flow (handlers for PlexAuthButton events) -----
|
// ----- OAuth flow (handlers for PlexAuthButton events) -----
|
||||||
async function handleAuthSuccess(authToken: string) {
|
async function handleAuthSuccess(authToken: string) {
|
||||||
try {
|
setPlexAuthCookie(authToken);
|
||||||
setPlexAuthCookie(authToken);
|
checkPlexConnection();
|
||||||
const userData = await fetchPlexUserData(authToken);
|
const success = await loadAll();
|
||||||
if (userData) {
|
|
||||||
plexUserData.value = userData;
|
if (success) {
|
||||||
plexUsername.value = userData.username;
|
messages.value.push({
|
||||||
isPlexConnected.value = true;
|
type: ErrorMessageTypes.Success,
|
||||||
}
|
title: "Authenticated with Plex",
|
||||||
const { success, message } = await linkPlexAccount(authToken);
|
message: "Successfully connected your Plex account"
|
||||||
if (success) {
|
} as IErrorMessage);
|
||||||
emit("reload");
|
} else {
|
||||||
await fetchPlexLibraries(authToken);
|
console.error("[PlexSettings] Error in handleAuthSuccess:");
|
||||||
messages.value.push({
|
|
||||||
type: ErrorMessageTypes.Success,
|
|
||||||
title: "Authenticated with Plex",
|
|
||||||
message: message || "Successfully connected your Plex account"
|
|
||||||
} as IErrorMessage);
|
|
||||||
} else {
|
|
||||||
messages.value.push({
|
|
||||||
type: ErrorMessageTypes.Error,
|
|
||||||
title: "Authentication failed",
|
|
||||||
message: message || "Could not connect to Plex"
|
|
||||||
} as IErrorMessage);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[PlexSettings] Error in handleAuthSuccess:", error);
|
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
type: ErrorMessageTypes.Error,
|
type: ErrorMessageTypes.Error,
|
||||||
title: "Authentication failed",
|
title: "Authentication failed",
|
||||||
@@ -259,35 +243,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----- Unlink flow -----
|
// ----- Unlink flow -----
|
||||||
function confirmUnlink() {
|
|
||||||
showConfirmModal.value = true;
|
|
||||||
}
|
|
||||||
function cancelUnlink() {
|
|
||||||
showConfirmModal.value = false;
|
|
||||||
}
|
|
||||||
async function unauthenticatePlex() {
|
async function unauthenticatePlex() {
|
||||||
showConfirmModal.value = false;
|
showUnlinkModal.value = false;
|
||||||
loading.value = true;
|
sessionStorage.removeItem("plex_user_data");
|
||||||
const response = await unlinkPlexAccount();
|
sessionStorage.removeItem("plex_server_data");
|
||||||
if (response?.success) {
|
sessionStorage.removeItem("plex_library_data");
|
||||||
localStorage.removeItem("plex_user_data");
|
sessionStorage.removeItem("plex_library_last_sync");
|
||||||
document.cookie =
|
document.cookie =
|
||||||
"plex_auth_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; SameSite=Strict";
|
"plex_auth_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; SameSite=Strict";
|
||||||
plexUserData.value = null;
|
plexUserData.value = null;
|
||||||
plexUsername.value = "";
|
plexUsername.value = "";
|
||||||
isPlexConnected.value = false;
|
showPlexInformation.value = false;
|
||||||
emit("reload");
|
emit("reload");
|
||||||
}
|
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
type: response.success
|
type: ErrorMessageTypes.Success,
|
||||||
? ErrorMessageTypes.Success
|
title: "Unlinked Plex account",
|
||||||
: ErrorMessageTypes.Error,
|
message: "All browser storage has been clear of plex account"
|
||||||
title: response.success
|
|
||||||
? "Unlinked Plex account"
|
|
||||||
: "Something went wrong",
|
|
||||||
message: response.message
|
|
||||||
} as IErrorMessage);
|
} as IErrorMessage);
|
||||||
loading.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Library modal -----
|
// ----- Library modal -----
|
||||||
@@ -304,39 +277,60 @@
|
|||||||
|
|
||||||
// ----- Sync -----
|
// ----- Sync -----
|
||||||
async function syncLibrary() {
|
async function syncLibrary() {
|
||||||
syncing.value = true;
|
const authToken = getPlexAuthCookie();
|
||||||
const authToken = getCookie("plex_auth_token");
|
|
||||||
if (!authToken) {
|
if (!authToken) {
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
type: ErrorMessageTypes.Error,
|
type: ErrorMessageTypes.Error,
|
||||||
title: "Sync failed",
|
title: "Sync failed",
|
||||||
message: "No authentication token found"
|
message: "No authentication token found"
|
||||||
} as IErrorMessage);
|
} as IErrorMessage);
|
||||||
syncing.value = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
await fetchPlexLibraries(authToken);
|
sessionStorage.removeItem("plex_user_data");
|
||||||
|
sessionStorage.removeItem("plex_server_data");
|
||||||
|
sessionStorage.removeItem("plex_library_data");
|
||||||
|
|
||||||
|
const success = await loadAll();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
type: ErrorMessageTypes.Success,
|
type: ErrorMessageTypes.Success,
|
||||||
title: "Library synced",
|
title: "Library synced",
|
||||||
message: "Your Plex library has been successfully synced"
|
message: "Your Plex library has been successfully synced"
|
||||||
} as IErrorMessage);
|
} as IErrorMessage);
|
||||||
} catch (error) {
|
} else {
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
type: ErrorMessageTypes.Error,
|
type: ErrorMessageTypes.Error,
|
||||||
title: "Sync failed",
|
title: "Sync failed",
|
||||||
message: "An error occurred while syncing your library"
|
message: "An error occurred while syncing your library"
|
||||||
} as IErrorMessage);
|
} as IErrorMessage);
|
||||||
} finally {
|
|
||||||
syncing.value = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
// ---- Helper load all ----
|
||||||
|
async function loadAll() {
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
loadPlexServer(),
|
||||||
|
loadPlexUserData(),
|
||||||
|
loadPlexLibraries()
|
||||||
|
]);
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("loadall error, some info might be missing");
|
||||||
|
}
|
||||||
|
|
||||||
checkPlexConnection();
|
checkPlexConnection();
|
||||||
loadPlexUserData();
|
return success;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// ---- Lifecycle functions ----
|
||||||
|
onMounted(loadAll);
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -139,7 +139,6 @@
|
|||||||
import IconMovie from "@/icons/IconMovie.vue";
|
import IconMovie from "@/icons/IconMovie.vue";
|
||||||
import IconActivity from "@/icons/IconActivity.vue";
|
import IconActivity from "@/icons/IconActivity.vue";
|
||||||
import IconProfile from "@/icons/IconProfile.vue";
|
import IconProfile from "@/icons/IconProfile.vue";
|
||||||
import IconRequest from "@/icons/IconRequest.vue";
|
|
||||||
import IconInbox from "@/icons/IconInbox.vue";
|
import IconInbox from "@/icons/IconInbox.vue";
|
||||||
import IconSearch from "@/icons/IconSearch.vue";
|
import IconSearch from "@/icons/IconSearch.vue";
|
||||||
import IconEdit from "@/icons/IconEdit.vue";
|
import IconEdit from "@/icons/IconEdit.vue";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
:class="{ active: active, fullwidth: fullWidth }"
|
:class="{ active: active, fullwidth: fullWidth }"
|
||||||
@click="emit('click')"
|
@click="event => emit('click', event)"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</button>
|
</button>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Emit {
|
interface Emit {
|
||||||
(e: "click");
|
(e: "click", event?: MouseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
defineProps<Props>();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<seasoned-button @click="submit">Register</seasoned-button>
|
<seasoned-button @click="submit">Register</seasoned-button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<router-link class="link" to="/signin"
|
<router-link class="link" to="/login"
|
||||||
>Have a user? Sign in here</router-link
|
>Have a user? Sign in here</router-link
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
@include mobile-only {
|
@include mobile-only {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
margin: 0 0 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
src/utils.ts
14
src/utils.ts
@@ -129,3 +129,17 @@ export function convertSecondsToHumanReadable(_value, values = null) {
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatNumber(n: number) {
|
||||||
|
if (!n?.toString()) return n;
|
||||||
|
|
||||||
|
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatBytes(bytes: number): string {
|
||||||
|
if (bytes === 0) return "0 Bytes";
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ["Bytes", "KB", "MB"];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user