mirror of
https://github.com/KevinMidboe/seasoned.git
synced 2026-06-10 14:53:14 +00:00
Compare commits
3 Commits
snyk-fix-4
...
snyk-fix-3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11f46f2860 | ||
| fb3b4c8f7d | |||
| 25da19eaf5 |
@@ -1,4 +1,4 @@
|
|||||||
FROM nginx:latest
|
FROM nginx:1.29.4-trixie
|
||||||
|
|
||||||
COPY public /usr/share/nginx/html
|
COPY public /usr/share/nginx/html
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf.template
|
COPY nginx.conf /etc/nginx/conf.d/default.conf.template
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"chart.js": "3.9.1",
|
"chart.js": "3.9.1",
|
||||||
"connect-history-api-fallback": "2.0.0",
|
"connect-history-api-fallback": "2.0.0",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"express": "4.19.2",
|
"express": "4.18.1",
|
||||||
"vue": "3.2.37",
|
"vue": "3.2.37",
|
||||||
"vue-router": "4.1.3",
|
"vue-router": "4.1.3",
|
||||||
"vuex": "4.0.2"
|
"vuex": "4.0.2"
|
||||||
|
|||||||
63
src/api.ts
63
src/api.ts
@@ -4,7 +4,7 @@ import type {
|
|||||||
IRequestSubmitResponse
|
IRequestSubmitResponse
|
||||||
} from "./interfaces/IRequestResponse";
|
} from "./interfaces/IRequestResponse";
|
||||||
|
|
||||||
const { ELASTIC, ELASTIC_INDEX } = process.env;
|
const { ELASTIC, ELASTIC_INDEX, ELASTIC_APIKEY } = process.env;
|
||||||
const API_HOSTNAME = window.location.origin;
|
const API_HOSTNAME = window.location.origin;
|
||||||
|
|
||||||
// - - - TMDB - - -
|
// - - - TMDB - - -
|
||||||
@@ -430,9 +430,13 @@ const unlinkPlexAccount = () => {
|
|||||||
|
|
||||||
// - - - User graphs - - -
|
// - - - User graphs - - -
|
||||||
|
|
||||||
const fetchGraphData = (urlPath, days, chartType) => {
|
const fetchGraphData = async (
|
||||||
|
urlPath: string,
|
||||||
|
days: number,
|
||||||
|
chartType: string
|
||||||
|
) => {
|
||||||
const url = new URL(`/api/v1/user/${urlPath}`, API_HOSTNAME);
|
const url = new URL(`/api/v1/user/${urlPath}`, API_HOSTNAME);
|
||||||
url.searchParams.append("days", days);
|
url.searchParams.append("days", String(days));
|
||||||
url.searchParams.append("y_axis", chartType);
|
url.searchParams.append("y_axis", chartType);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => {
|
return fetch(url.href).then(resp => {
|
||||||
@@ -447,7 +451,7 @@ const fetchGraphData = (urlPath, days, chartType) => {
|
|||||||
|
|
||||||
// - - - Random emoji - - -
|
// - - - Random emoji - - -
|
||||||
|
|
||||||
const getEmoji = () => {
|
const getEmoji = async () => {
|
||||||
const url = new URL("/api/v1/emoji", API_HOSTNAME);
|
const url = new URL("/api/v1/emoji", API_HOSTNAME);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href)
|
||||||
@@ -468,33 +472,58 @@ const getEmoji = () => {
|
|||||||
* @param {string} query
|
* @param {string} query
|
||||||
* @returns {object} List of movies and shows matching query
|
* @returns {object} List of movies and shows matching query
|
||||||
*/
|
*/
|
||||||
const elasticSearchMoviesAndShows = (query, count = 22) => {
|
const elasticSearchMoviesAndShows = (query: string, count = 22) => {
|
||||||
const url = new URL(`${ELASTIC_INDEX}/_search`, ELASTIC);
|
const url = new URL(`${ELASTIC_INDEX}/_search`, ELASTIC);
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
sort: [{ popularity: { order: "desc" } }, "_score"],
|
sort: [{ popularity: { order: "desc" } }, "_score"],
|
||||||
|
size: count,
|
||||||
query: {
|
query: {
|
||||||
bool: {
|
multi_match: {
|
||||||
should: [
|
query,
|
||||||
{
|
fields: ["name", "original_title", "original_name"],
|
||||||
match_phrase_prefix: {
|
type: "phrase_prefix",
|
||||||
original_name: query
|
tie_breaker: 0.3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
suggest: {
|
||||||
match_phrase_prefix: {
|
text: query,
|
||||||
original_title: query
|
"person-suggest": {
|
||||||
|
prefix: query,
|
||||||
|
completion: {
|
||||||
|
field: "name.completion",
|
||||||
|
fuzzy: {
|
||||||
|
fuzziness: "AUTO"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
size: count
|
"movie-suggest": {
|
||||||
|
prefix: query,
|
||||||
|
completion: {
|
||||||
|
field: "original_title.completion",
|
||||||
|
fuzzy: {
|
||||||
|
fuzziness: "AUTO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"show-suggest": {
|
||||||
|
prefix: query,
|
||||||
|
completion: {
|
||||||
|
field: "original_name.completion",
|
||||||
|
fuzzy: {
|
||||||
|
fuzziness: "AUTO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: {
|
||||||
|
Authorization: `ApiKey ${ELASTIC_APIKEY}`,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from "vue";
|
|
||||||
import CastListItem from "src/components/CastListItem.vue";
|
import CastListItem from "src/components/CastListItem.vue";
|
||||||
import type {
|
import type {
|
||||||
IMovie,
|
IMovie,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import type { ICast, ICrew, IMovie, IShow } from "../interfaces/IList";
|
import type { ICast, ICrew, IMovie, IShow } from "../interfaces/IList";
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, defineProps, onMounted, watch } from "vue";
|
import { ref, onMounted, watch } from "vue";
|
||||||
import {
|
import {
|
||||||
Chart,
|
Chart,
|
||||||
LineElement,
|
LineElement,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from "vue";
|
|
||||||
import ResultsListItem from "@/components/ResultsListItem.vue";
|
import ResultsListItem from "@/components/ResultsListItem.vue";
|
||||||
import type { ListResults } from "../interfaces/IList";
|
import type { ListResults } from "../interfaces/IList";
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, defineProps, onMounted } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import type { IMovie, IShow, IPerson } from "../interfaces/IList";
|
import type { IMovie, IShow, IPerson } from "../interfaces/IList";
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, ref, computed, onMounted } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
import PageHeader from "@/components/PageHeader.vue";
|
import PageHeader from "@/components/PageHeader.vue";
|
||||||
import ResultsList from "@/components/ResultsList.vue";
|
import ResultsList from "@/components/ResultsList.vue";
|
||||||
import SeasonedButton from "@/components/ui/SeasonedButton.vue";
|
import SeasonedButton from "@/components/ui/SeasonedButton.vue";
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
>
|
>
|
||||||
<IconMovie v-if="result.type == 'movie'" class="type-icon" />
|
<IconMovie v-if="result.type == 'movie'" class="type-icon" />
|
||||||
<IconShow v-if="result.type == 'show'" class="type-icon" />
|
<IconShow v-if="result.type == 'show'" class="type-icon" />
|
||||||
|
<IconPerson v-if="result.type == 'person'" class="type-icon" />
|
||||||
<span class="title">{{ result.title }}</span>
|
<span class="title">{{ result.title }}</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@@ -23,18 +24,25 @@
|
|||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Searches Elasticsearch for results based on changes to `query`.
|
||||||
|
-->
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, defineProps } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import IconMovie from "@/icons/IconMovie.vue";
|
import IconMovie from "@/icons/IconMovie.vue";
|
||||||
import IconShow from "@/icons/IconShow.vue";
|
import IconShow from "@/icons/IconShow.vue";
|
||||||
|
import IconPerson from "@/icons/IconPerson.vue";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import { elasticSearchMoviesAndShows } from "../../api";
|
import { elasticSearchMoviesAndShows } from "../../api";
|
||||||
import { MediaTypes } from "../../interfaces/IList";
|
import { MediaTypes } from "../../interfaces/IList";
|
||||||
import { Index } from "../../interfaces/IAutocompleteSearch";
|
|
||||||
import type {
|
import type {
|
||||||
IAutocompleteResult,
|
IAutocompleteResult,
|
||||||
IAutocompleteSearchResults
|
IAutocompleteSearchResults,
|
||||||
|
Hit,
|
||||||
|
Option,
|
||||||
|
Source
|
||||||
} from "../../interfaces/IAutocompleteSearch";
|
} from "../../interfaces/IAutocompleteSearch";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -48,6 +56,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const numberOfResults = 10;
|
const numberOfResults = 10;
|
||||||
|
let timeoutId = null;
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
const emit = defineEmits<Emit>();
|
const emit = defineEmits<Emit>();
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
@@ -55,23 +64,9 @@
|
|||||||
const searchResults: Ref<Array<IAutocompleteResult>> = ref([]);
|
const searchResults: Ref<Array<IAutocompleteResult>> = ref([]);
|
||||||
const keyboardNavigationIndex: Ref<number> = ref(0);
|
const keyboardNavigationIndex: Ref<number> = ref(0);
|
||||||
|
|
||||||
watch(
|
function removeDuplicates(_searchResults: Array<IAutocompleteResult>) {
|
||||||
() => props.query,
|
|
||||||
newQuery => {
|
|
||||||
if (newQuery?.length > 0)
|
|
||||||
fetchAutocompleteResults(); /* eslint-disable-line no-use-before-define */
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function openPopup(result) {
|
|
||||||
if (!result.id || !result.type) return;
|
|
||||||
|
|
||||||
store.dispatch("popup/open", { ...result });
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDuplicates(_searchResults) {
|
|
||||||
const filteredResults = [];
|
const filteredResults = [];
|
||||||
_searchResults.forEach(result => {
|
_searchResults.forEach((result: IAutocompleteResult) => {
|
||||||
if (result === undefined) return;
|
if (result === undefined) return;
|
||||||
const numberOfDuplicates = filteredResults.filter(
|
const numberOfDuplicates = filteredResults.filter(
|
||||||
filterItem => filterItem.id === result.id
|
filterItem => filterItem.id === result.id
|
||||||
@@ -86,34 +81,43 @@
|
|||||||
return filteredResults;
|
return filteredResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
function elasticIndexToMediaType(index: Index): MediaTypes {
|
function convertMediaType(type: string | null): MediaTypes | null {
|
||||||
if (index === Index.Movies) return MediaTypes.Movie;
|
if (type === "movie") return MediaTypes.Movie;
|
||||||
if (index === Index.Shows) return MediaTypes.Show;
|
|
||||||
|
if (type === "tv_series") return MediaTypes.Show;
|
||||||
|
|
||||||
|
if (type === "person") return MediaTypes.Person;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseElasticResponse(elasticResponse: IAutocompleteSearchResults) {
|
function parseElasticResponse(elasticResponse: IAutocompleteSearchResults) {
|
||||||
const data = elasticResponse.hits.hits;
|
const elasticResults = elasticResponse.hits.hits;
|
||||||
|
const suggestResults = elasticResponse.suggest["movie-suggest"][0].options;
|
||||||
|
|
||||||
|
let data: Array<Source> = elasticResults.map((el: Hit) => el._source);
|
||||||
|
data = data.concat(suggestResults.map((el: Option) => el._source));
|
||||||
|
|
||||||
|
// data = data.concat(elasticResponse['suggest']['person-suggest'][0]['options'])
|
||||||
|
// data = data.concat(elasticResponse['suggest']['show-suggest'][0]['options'])
|
||||||
|
data = data.sort((a, b) => (a.popularity < b.popularity ? 1 : -1));
|
||||||
|
|
||||||
const results: Array<IAutocompleteResult> = [];
|
const results: Array<IAutocompleteResult> = [];
|
||||||
|
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
if (!Object.values(Index).includes(item._index)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
results.push({
|
results.push({
|
||||||
title: item._source?.original_name || item._source.original_title,
|
title: item?.original_name || item?.original_title || item?.name,
|
||||||
id: item._source.id,
|
id: item.id,
|
||||||
adult: item._source.adult,
|
adult: item.adult,
|
||||||
type: elasticIndexToMediaType(item._index)
|
type: convertMediaType(item?.type)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return removeDuplicates(results).map((el, index) => {
|
return removeDuplicates(results)
|
||||||
|
.map((el, index) => {
|
||||||
return { ...el, index };
|
return { ...el, index };
|
||||||
});
|
})
|
||||||
|
.slice(0, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchAutocompleteResults() {
|
function fetchAutocompleteResults() {
|
||||||
@@ -123,11 +127,34 @@
|
|||||||
elasticSearchMoviesAndShows(props.query, numberOfResults)
|
elasticSearchMoviesAndShows(props.query, numberOfResults)
|
||||||
.then(elasticResponse => parseElasticResponse(elasticResponse))
|
.then(elasticResponse => parseElasticResponse(elasticResponse))
|
||||||
.then(_searchResults => {
|
.then(_searchResults => {
|
||||||
|
console.log(_searchResults);
|
||||||
emit("update:results", _searchResults);
|
emit("update:results", _searchResults);
|
||||||
searchResults.value = _searchResults;
|
searchResults.value = _searchResults;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const debounce = (callback: () => void, wait: number) => {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
timeoutId = window.setTimeout(() => {
|
||||||
|
callback();
|
||||||
|
}, wait);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.query,
|
||||||
|
newQuery => {
|
||||||
|
if (newQuery?.length > 0) {
|
||||||
|
debounce(fetchAutocompleteResults, 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function openPopup(result: IAutocompleteResult) {
|
||||||
|
if (!result.id || !result.type) return;
|
||||||
|
|
||||||
|
store.dispatch("popup/open", { ...result });
|
||||||
|
}
|
||||||
|
|
||||||
// on load functions
|
// on load functions
|
||||||
fetchAutocompleteResults();
|
fetchAutocompleteResults();
|
||||||
// end on load functions
|
// end on load functions
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import { computed, defineProps } from "vue";
|
import { computed } from "vue";
|
||||||
import type INavigationIcon from "../../interfaces/INavigationIcon";
|
import type INavigationIcon from "../../interfaces/INavigationIcon";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- Handles constructing markup and state for dropdown.
|
||||||
|
|
||||||
|
Markup:
|
||||||
|
Consist of: search icon, input & close button.
|
||||||
|
|
||||||
|
State:
|
||||||
|
State is passing input variable `query` to dropdown and carrying state
|
||||||
|
of selected dropdown element as variable `index`. This is because
|
||||||
|
index is manipulated based on arrow key events from same input as
|
||||||
|
the `query`.
|
||||||
|
-->
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
@@ -51,6 +63,7 @@
|
|||||||
import IconClose from "@/icons/IconClose.vue";
|
import IconClose from "@/icons/IconClose.vue";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import type { MediaTypes } from "../../interfaces/IList";
|
import type { MediaTypes } from "../../interfaces/IList";
|
||||||
|
import { IAutocompleteResult } from "../../interfaces/IAutocompleteSearch";
|
||||||
|
|
||||||
interface ISearchResult {
|
interface ISearchResult {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -66,7 +79,7 @@
|
|||||||
const query: Ref<string> = ref(null);
|
const query: Ref<string> = ref(null);
|
||||||
const disabled: Ref<boolean> = ref(false);
|
const disabled: Ref<boolean> = ref(false);
|
||||||
const dropdownIndex: Ref<number> = ref(-1);
|
const dropdownIndex: Ref<number> = ref(-1);
|
||||||
const dropdownResults: Ref<ISearchResult[]> = ref([]);
|
const dropdownResults: Ref<IAutocompleteResult[]> = ref([]);
|
||||||
const inputIsActive: Ref<boolean> = ref(false);
|
const inputIsActive: Ref<boolean> = ref(false);
|
||||||
const inputElement: Ref<HTMLInputElement> = ref(null);
|
const inputElement: Ref<HTMLInputElement> = ref(null);
|
||||||
|
|
||||||
@@ -85,8 +98,13 @@
|
|||||||
query.value = decodeURIComponent(params.get("query"));
|
query.value = decodeURIComponent(params.get("query"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ELASTIC } = process.env;
|
const { ELASTIC, ELASTIC_APIKEY } = process.env;
|
||||||
if (ELASTIC === undefined || ELASTIC === "") {
|
if (
|
||||||
|
ELASTIC === undefined ||
|
||||||
|
ELASTIC === "" ||
|
||||||
|
ELASTIC_APIKEY === undefined ||
|
||||||
|
ELASTIC_APIKEY === ""
|
||||||
|
) {
|
||||||
disabled.value = true;
|
disabled.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +163,7 @@
|
|||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
if (!query.value || query.value.length === 0) return;
|
if (!query.value || query.value.length === 0) return;
|
||||||
|
|
||||||
|
// if index is set, navigation has happened. Open popup else search
|
||||||
if (dropdownIndex.value >= 0) {
|
if (dropdownIndex.value >= 0) {
|
||||||
const resultItem = dropdownResults.value[dropdownIndex.value];
|
const resultItem = dropdownResults.value[dropdownIndex.value];
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, defineEmits } from "vue";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, defineProps, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import IconArrowDown from "../../icons/IconArrowDown.vue";
|
import IconArrowDown from "../../icons/IconArrowDown.vue";
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from "vue";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
detail?: string | number;
|
detail?: string | number;
|
||||||
|
|||||||
@@ -165,7 +165,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, defineProps, onMounted } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
// import img from "@/directives/v-image";
|
// import img from "@/directives/v-image";
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, defineProps } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import CastList from "@/components/CastList.vue";
|
import CastList from "@/components/CastList.vue";
|
||||||
import Detail from "@/components/popup/Detail.vue";
|
import Detail from "@/components/popup/Detail.vue";
|
||||||
import Description from "@/components/popup/Description.vue";
|
import Description from "@/components/popup/Description.vue";
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, defineEmits } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import seasonedInput from "@/components/ui/SeasonedInput.vue";
|
import seasonedInput from "@/components/ui/SeasonedInput.vue";
|
||||||
import SeasonedButton from "@/components/ui/SeasonedButton.vue";
|
import SeasonedButton from "@/components/ui/SeasonedButton.vue";
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, inject, defineProps } from "vue";
|
import { ref, watch, inject } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import Loader from "@/components/ui/Loader.vue";
|
import Loader from "@/components/ui/Loader.vue";
|
||||||
import TorrentTable from "@/components/torrent/TorrentTable.vue";
|
import TorrentTable from "@/components/torrent/TorrentTable.vue";
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, defineProps, defineEmits } from "vue";
|
import { ref } from "vue";
|
||||||
import IconMagnet from "@/icons/IconMagnet.vue";
|
import IconMagnet from "@/icons/IconMagnet.vue";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import { sortableSize } from "../../utils";
|
import { sortableSize } from "../../utils";
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, defineProps } from "vue";
|
import { ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import TorrentSearchResults from "@/components/torrent/TorrentSearchResults.vue";
|
import TorrentSearchResults from "@/components/torrent/TorrentSearchResults.vue";
|
||||||
import SeasonedButton from "@/components/ui/SeasonedButton.vue";
|
import SeasonedButton from "@/components/ui/SeasonedButton.vue";
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
--></template>
|
--></template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from "vue";
|
|
||||||
import LoaderHeightType from "../../interfaces/ILoader";
|
import LoaderHeightType from "../../interfaces/ILoader";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from "vue";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
count?: number;
|
count?: number;
|
||||||
lineClass?: string;
|
lineClass?: string;
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, defineEmits } from "vue";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
fullWidth?: boolean;
|
fullWidth?: boolean;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, defineProps, defineEmits } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import IconKey from "@/icons/IconKey.vue";
|
import IconKey from "@/icons/IconKey.vue";
|
||||||
import IconEmail from "@/icons/IconEmail.vue";
|
import IconEmail from "@/icons/IconEmail.vue";
|
||||||
import IconBinoculars from "@/icons/IconBinoculars.vue";
|
import IconBinoculars from "@/icons/IconBinoculars.vue";
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, defineEmits } from "vue";
|
|
||||||
import type {
|
import type {
|
||||||
ErrorMessageTypes,
|
ErrorMessageTypes,
|
||||||
IErrorMessage
|
IErrorMessage
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, defineEmits } from "vue";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
options: string[];
|
options: string[];
|
||||||
selected?: string;
|
selected?: string;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface IAutocompleteSearchResults {
|
|||||||
timed_out: boolean;
|
timed_out: boolean;
|
||||||
_shards: Shards;
|
_shards: Shards;
|
||||||
hits: Hits;
|
hits: Hits;
|
||||||
|
suggest: Suggest;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Shards {
|
export interface Shards {
|
||||||
@@ -37,6 +38,27 @@ export interface Hit {
|
|||||||
sort: number[];
|
sort: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Suggest {
|
||||||
|
"movie-suggest": SuggestOptions[];
|
||||||
|
"person-suggest": SuggestOptions[];
|
||||||
|
"show-suggest": SuggestOptions[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SuggestOptions {
|
||||||
|
text: string;
|
||||||
|
offset: number;
|
||||||
|
length: number;
|
||||||
|
options: Option[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Option {
|
||||||
|
text: string;
|
||||||
|
_index: string;
|
||||||
|
_id: string;
|
||||||
|
_score: number;
|
||||||
|
_source: Source;
|
||||||
|
}
|
||||||
|
|
||||||
export enum Index {
|
export enum Index {
|
||||||
Movies = "movies",
|
Movies = "movies",
|
||||||
Shows = "shows"
|
Shows = "shows"
|
||||||
@@ -57,6 +79,8 @@ export interface Source {
|
|||||||
agent: Agent;
|
agent: Agent;
|
||||||
original_title: string;
|
original_title: string;
|
||||||
original_name?: string;
|
original_name?: string;
|
||||||
|
name?: string;
|
||||||
|
type?: MediaTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Agent {
|
export interface Agent {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, defineProps, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
|
|
||||||
|
|||||||
79
yarn.lock
79
yarn.lock
@@ -2775,24 +2775,6 @@ body-parser@1.20.0, body-parser@^1.19.0:
|
|||||||
type-is "~1.6.18"
|
type-is "~1.6.18"
|
||||||
unpipe "1.0.0"
|
unpipe "1.0.0"
|
||||||
|
|
||||||
body-parser@1.20.2:
|
|
||||||
version "1.20.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
|
||||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
|
||||||
dependencies:
|
|
||||||
bytes "3.1.2"
|
|
||||||
content-type "~1.0.5"
|
|
||||||
debug "2.6.9"
|
|
||||||
depd "2.0.0"
|
|
||||||
destroy "1.2.0"
|
|
||||||
http-errors "2.0.0"
|
|
||||||
iconv-lite "0.4.24"
|
|
||||||
on-finished "2.4.1"
|
|
||||||
qs "6.11.0"
|
|
||||||
raw-body "2.5.2"
|
|
||||||
type-is "~1.6.18"
|
|
||||||
unpipe "1.0.0"
|
|
||||||
|
|
||||||
body@^5.1.0:
|
body@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069"
|
resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069"
|
||||||
@@ -3452,11 +3434,6 @@ content-type@~1.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||||
|
|
||||||
content-type@~1.0.5:
|
|
||||||
version "1.0.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
|
|
||||||
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
|
|
||||||
|
|
||||||
continuable-cache@^0.3.1:
|
continuable-cache@^0.3.1:
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f"
|
resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f"
|
||||||
@@ -3642,11 +3619,6 @@ cookie@0.5.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||||
|
|
||||||
cookie@0.6.0:
|
|
||||||
version "0.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
|
||||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
|
||||||
|
|
||||||
copy-descriptor@^0.1.0:
|
copy-descriptor@^0.1.0:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||||
@@ -4778,44 +4750,7 @@ express-history-api-fallback@^2.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/express-history-api-fallback/-/express-history-api-fallback-2.2.1.tgz#3a2ad27f7bebc90fc533d110d7c6d83097bcd057"
|
resolved "https://registry.yarnpkg.com/express-history-api-fallback/-/express-history-api-fallback-2.2.1.tgz#3a2ad27f7bebc90fc533d110d7c6d83097bcd057"
|
||||||
integrity sha512-swxwm3aP8vrOOvlzOdZvHlSZtJGwHKaY94J6AkrAgCTmcbko3IRwbkhLv2wKV1WeZhjxX58aLMpP3atDBnKuZg==
|
integrity sha512-swxwm3aP8vrOOvlzOdZvHlSZtJGwHKaY94J6AkrAgCTmcbko3IRwbkhLv2wKV1WeZhjxX58aLMpP3atDBnKuZg==
|
||||||
|
|
||||||
express@4.19.2:
|
express@4.18.1, express@^4.17.1, express@^4.17.3:
|
||||||
version "4.19.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
|
||||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
|
||||||
dependencies:
|
|
||||||
accepts "~1.3.8"
|
|
||||||
array-flatten "1.1.1"
|
|
||||||
body-parser "1.20.2"
|
|
||||||
content-disposition "0.5.4"
|
|
||||||
content-type "~1.0.4"
|
|
||||||
cookie "0.6.0"
|
|
||||||
cookie-signature "1.0.6"
|
|
||||||
debug "2.6.9"
|
|
||||||
depd "2.0.0"
|
|
||||||
encodeurl "~1.0.2"
|
|
||||||
escape-html "~1.0.3"
|
|
||||||
etag "~1.8.1"
|
|
||||||
finalhandler "1.2.0"
|
|
||||||
fresh "0.5.2"
|
|
||||||
http-errors "2.0.0"
|
|
||||||
merge-descriptors "1.0.1"
|
|
||||||
methods "~1.1.2"
|
|
||||||
on-finished "2.4.1"
|
|
||||||
parseurl "~1.3.3"
|
|
||||||
path-to-regexp "0.1.7"
|
|
||||||
proxy-addr "~2.0.7"
|
|
||||||
qs "6.11.0"
|
|
||||||
range-parser "~1.2.1"
|
|
||||||
safe-buffer "5.2.1"
|
|
||||||
send "0.18.0"
|
|
||||||
serve-static "1.15.0"
|
|
||||||
setprototypeof "1.2.0"
|
|
||||||
statuses "2.0.1"
|
|
||||||
type-is "~1.6.18"
|
|
||||||
utils-merge "1.0.1"
|
|
||||||
vary "~1.1.2"
|
|
||||||
|
|
||||||
express@^4.17.1, express@^4.17.3:
|
|
||||||
version "4.18.1"
|
version "4.18.1"
|
||||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf"
|
resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf"
|
||||||
integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==
|
integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==
|
||||||
@@ -8565,7 +8500,7 @@ qs@6.10.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
qs@6.11.0, qs@^6.4.0, qs@^6.9.4:
|
qs@^6.4.0, qs@^6.9.4:
|
||||||
version "6.11.0"
|
version "6.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
|
||||||
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
|
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
|
||||||
@@ -8623,16 +8558,6 @@ raw-body@2.5.1:
|
|||||||
iconv-lite "0.4.24"
|
iconv-lite "0.4.24"
|
||||||
unpipe "1.0.0"
|
unpipe "1.0.0"
|
||||||
|
|
||||||
raw-body@2.5.2:
|
|
||||||
version "2.5.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
|
|
||||||
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
|
|
||||||
dependencies:
|
|
||||||
bytes "3.1.2"
|
|
||||||
http-errors "2.0.0"
|
|
||||||
iconv-lite "0.4.24"
|
|
||||||
unpipe "1.0.0"
|
|
||||||
|
|
||||||
raw-body@~1.1.0:
|
raw-body@~1.1.0:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425"
|
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425"
|
||||||
|
|||||||
Reference in New Issue
Block a user