Skip to content

Commit c359926

Browse files
committed
Feat: make it possible to import specific library.
1 parent 6f44acd commit c359926

File tree

7 files changed

+325
-48
lines changed

7 files changed

+325
-48
lines changed

frontend/app/pages/backend/[backend]/libraries.vue

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,23 @@
7272
<input :id="`ignore-${item.id}`" type="checkbox" class="switch is-success" :checked="item.ignored"
7373
@change="toggleIgnore(item)">
7474
<label :for="`ignore-${item.id}`"></label>
75-
<span>Ignore content from this library.</span>
75+
<span>Ignore library content.</span>
7676
</div>
7777
<div class="card-footer-item">
78-
<NuxtLink :to="`/backend/${backend}/stale/${item.id}?name=${item.title}`">
79-
<span class="icon-text">
80-
<span class="icon"><i class="fas fa-sync"></i></span>
81-
<span>Check Content Staleness</span>
82-
</span>
83-
</NuxtLink>
78+
<div class="control is-fullwidth has-icons-left" v-if="item.supported && !item.ignored">
79+
<div class="select is-fullwidth">
80+
<select v-model="selectedCommand" @change="forwardCommand(item)">
81+
<option value="" disabled>Quick operations</option>
82+
<option v-for="(command, index) in usefulCommands" :key="`ql-${item.id}-${index}`" :value="index">
83+
{{ command.id }}. {{ command.title }}
84+
</option>
85+
</select>
86+
</div>
87+
<div class="icon is-left">
88+
<i class="fas fa-terminal" />
89+
</div>
90+
</div>
91+
<span v-else>-</span>
8492
</div>
8593
</div>
8694
</div>
@@ -103,17 +111,43 @@
103111

104112
<script setup lang="ts">
105113
import { ref, onMounted } from 'vue'
106-
import { useRoute, useHead } from '#app'
114+
import { useRoute, useHead, navigateTo } from '#app'
107115
import { useStorage } from '@vueuse/core'
108-
import { request, notification, parse_api_response } from '~/utils'
116+
import { request, notification, parse_api_response, makeConsoleCommand, r } from '~/utils'
109117
import Message from '~/components/Message.vue'
110-
import type { LibraryItem } from '~/types'
118+
import type { LibraryItem, JsonObject, JsonValue, UtilityCommand } from '~/types'
111119
112120
const route = useRoute()
113121
const backend = route.params.backend as string
114122
const items = ref<Array<LibraryItem>>([])
115123
const isLoading = ref<boolean>(false)
116124
const show_page_tips = useStorage('show_page_tips', true)
125+
const api_user = useStorage('api_user', 'main')
126+
const selectedCommand = ref<string>('')
127+
128+
type UsefulCommand = UtilityCommand
129+
130+
type UsefulCommands = Record<string, UsefulCommand>
131+
132+
type CommandUtility = {
133+
user: string
134+
backend: string
135+
library_id: string
136+
[key: string]: JsonValue | undefined
137+
}
138+
139+
const usefulCommands: UsefulCommands = {
140+
import_library: {
141+
id: 1,
142+
title: 'Import data from this library.',
143+
command: 'state:import -v -u {user} -s {backend} -S {library_id}',
144+
},
145+
force_import_library: {
146+
id: 2,
147+
title: 'Force import from this library.',
148+
command: 'state:import -f -v -u {user} -s {backend} -S {library_id}',
149+
},
150+
}
117151
118152
useHead({ title: `Backends: ${backend} - Libraries` })
119153
@@ -139,6 +173,28 @@ const loadContent = async (): Promise<void> => {
139173
}
140174
}
141175
176+
const forwardCommand = async (library: LibraryItem): Promise<void> => {
177+
if ('' === selectedCommand.value) {
178+
return
179+
}
180+
181+
const index = selectedCommand.value as keyof UsefulCommands
182+
selectedCommand.value = ''
183+
184+
const command = usefulCommands[index]
185+
if (!command) {
186+
return
187+
}
188+
189+
const util: CommandUtility = {
190+
user: api_user.value,
191+
backend,
192+
library_id: String(library.id),
193+
}
194+
195+
await navigateTo(makeConsoleCommand(r(command.command, util as unknown as JsonObject)))
196+
}
197+
142198
const toggleIgnore = async (library: LibraryItem): Promise<void> => {
143199
try {
144200
const newState = !library.ignored
@@ -152,7 +208,6 @@ const toggleIgnore = async (library: LibraryItem): Promise<void> => {
152208
return
153209
}
154210
155-
notification('success', 'Success', `Library '${library.title}' has been ${newState ? 'ignored' : 'un-ignored'}.`)
156211
const libraryIndex = items.value.findIndex(b => b.id === library.id)
157212
if (-1 !== libraryIndex && items.value[libraryIndex]) {
158213
items.value[libraryIndex].ignored = !library.ignored

src/Backends/Jellyfin/Action/Import.php

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
243243

244244
$limitLibraryId = ag($opts, Options::ONLY_LIBRARY_ID, null);
245245

246+
$selectLibraryList = null;
247+
$inverseLibrarySelect = true === (bool) ag($context->options, Options::LIBRARY_INVERSE, false);
248+
if (null !== ($selectLibraryIds = ag($context->options, Options::LIBRARY_SELECT, null))) {
249+
$selectLibraryList = array_map(static fn($value) => (string) $value, $selectLibraryIds);
250+
}
251+
246252
$requests = $total = [];
247253
$ignored = $unsupported = 0;
248254

@@ -259,11 +265,15 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
259265
],
260266
];
261267

262-
if (null !== $limitLibraryId && $libraryId !== (string) $limitLibraryId) {
268+
if ($limitLibraryId && $libraryId !== (string) $limitLibraryId) {
269+
continue;
270+
}
271+
272+
if (true === in_array($libraryId, $ignoreIds ?? [], true)) {
263273
continue;
264274
}
265275

266-
if (true === in_array(ag($logContext, 'library.id'), $ignoreIds ?? [], true)) {
276+
if ($selectLibraryList && $inverseLibrarySelect === in_array($libraryId, $selectLibraryList, true)) {
267277
continue;
268278
}
269279

@@ -280,7 +290,7 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
280290
http_build_query([
281291
'sortBy' => 'DateCreated',
282292
'sortOrder' => 'Ascending',
283-
'parentId' => ag($logContext, 'library.id'),
293+
'parentId' => $libraryId,
284294
'recursive' => 'true',
285295
'collapseBoxSetItems' => 'false',
286296
'excludeLocationTypes' => 'Virtual',
@@ -384,10 +394,11 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
384394

385395
// -- Episodes Parent external ids.
386396
foreach ($listDirs as $section) {
397+
$libraryId = (string) ag($section, 'Id');
387398
$logContext = [
388399
...$rContext,
389400
'library' => [
390-
'id' => (string) ag($section, 'Id'),
401+
'id' => $libraryId,
391402
'title' => ag($section, 'Name', '??'),
392403
'type' => ag($section, ['CollectionType', 'Type'], 'unknown'),
393404
],
@@ -397,7 +408,11 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
397408
],
398409
];
399410

400-
if (true === in_array(ag($logContext, 'library.id'), $ignoreIds ?? [], true)) {
411+
if (true === in_array($libraryId, $ignoreIds ?? [], true)) {
412+
continue;
413+
}
414+
415+
if ($selectLibraryList && $inverseLibrarySelect === in_array($libraryId, $selectLibraryList, true)) {
401416
continue;
402417
}
403418

@@ -409,8 +424,8 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
409424
continue;
410425
}
411426

412-
if (true === array_key_exists(ag($logContext, 'library.id'), $total)) {
413-
$logContext['library']['totalRecords'] = $total[ag($logContext, 'library.id')];
427+
if (true === array_key_exists($libraryId, $total)) {
428+
$logContext['library']['totalRecords'] = $total[$libraryId];
414429
}
415430

416431
$url = $context
@@ -422,7 +437,7 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
422437
)
423438
->withQuery(
424439
http_build_query([
425-
'parentId' => ag($logContext, 'library.id'),
440+
'parentId' => $libraryId,
426441
'recursive' => 'false',
427442
'enableUserData' => 'false',
428443
'enableImages' => 'false',
@@ -462,16 +477,17 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
462477

463478
// -- get paginated movies/episodes.
464479
foreach ($listDirs as $section) {
480+
$libraryId = (string) ag($section, 'Id');
465481
$logContext = [
466482
...$rContext,
467483
'library' => [
468-
'id' => (string) ag($section, 'Id'),
484+
'id' => $libraryId,
469485
'title' => ag($section, 'Name', '??'),
470486
'type' => ag($section, ['CollectionType', 'Type'], 'unknown'),
471487
],
472488
];
473489

474-
if (true === in_array(ag($logContext, 'library.id'), $ignoreIds ?? [], true)) {
490+
if (true === in_array($libraryId, $ignoreIds ?? [], true)) {
475491
$ignored++;
476492
$this->logger->info(
477493
message: "{action}: Ignoring '{client}: {user}@{backend}' - '{library.title}'. Requested by user.",
@@ -480,6 +496,14 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
480496
continue;
481497
}
482498

499+
if ($selectLibraryList && $inverseLibrarySelect === in_array($libraryId, $selectLibraryList, true)) {
500+
$this->logger->info(
501+
message: "{action}: Excluding '{client}: {user}@{backend}' - '{library.title}'. Requested by user.",
502+
context: $logContext,
503+
);
504+
continue;
505+
}
506+
483507
if (!in_array(ag($logContext, 'library.type'), $types, true)) {
484508
$unsupported++;
485509
$this->logger->info(
@@ -489,7 +513,7 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
489513
continue;
490514
}
491515

492-
if (false === array_key_exists(ag($logContext, 'library.id'), $total)) {
516+
if (false === array_key_exists($libraryId, $total)) {
493517
$ignored++;
494518
$this->logger->warning(
495519
message: "{action}: Ignoring '{client}: {user}@{backend}' - '{library.title}'. No items count was found.",
@@ -498,9 +522,9 @@ protected function getLibraries(Context $context, Closure $handle, Closure $erro
498522
continue;
499523
}
500524

501-
$logContext['library']['totalRecords'] = $total[ag($logContext, 'library.id')];
525+
$logContext['library']['totalRecords'] = $total[$libraryId];
502526

503-
$segmentTotal = (int) $total[ag($logContext, 'library.id')];
527+
$segmentTotal = (int) $total[$libraryId];
504528
$segmentSize = (int) ag($context->options, Options::LIBRARY_SEGMENT, 1000);
505529
$segmented = ceil($segmentTotal / $segmentSize);
506530

0 commit comments

Comments
 (0)