aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/video-app-settings.js
blob: b2d50156e6504d070311d43c0d6710e736b1dace (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { html, useState, useEffect } from './vendor/htm-preact.js';
import { t, getLocale, LOCALES } from './i18n.js';
import { ToggleSwitch, useSaver } from './settings-ui.js';
import { FolderPickerField } from './folder-tree.js';

// MeshBay's own locale codes (i18n.js LOCALES) to the language tag TMDB
// expects. Duplicated from nothing: this is the only place it lives now that
// the TMDB fields moved out of the monolithic settings page.
const TMDB_LANGUAGE_BY_LOCALE = {
  en: 'en-US', fr: 'fr-FR', es: 'es-ES', 'pt-BR': 'pt-BR', 'zh-CN': 'zh-CN',
  ja: 'ja-JP', de: 'de-DE', it: 'it-IT', nl: 'nl-NL', pl: 'pl-PL',
};

/**
 * The Videos app's operator settings: which folders it works over, and the
 * TMDB lookups it makes.
 *
 * Several folders now, not one. A film library is as likely to be two drives
 * as one, and the single-root model made the second one invisible — the
 * operator's only recourse was to point Videos at a parent containing both,
 * which pulls in everything else under it too.
 *
 * The TMDB parts are two independent settings that happen to sit together:
 * the on/off switch is per group, while the API key and the query language
 * are node-wide, because they are one operator's credential and one shared
 * cache (docs/mediacenter.md §5.5). They save separately for that reason.
 */
function VideoSettings({ roots, dirs, settings, saveDirectories, transport, signFn }) {
  const { busy, msg, run } = useSaver();
  const [directories, setDirectories] = useState(settings.videoDirectories || []);
  const [tmdbEnabled, setTmdbEnabled] = useState(settings.tmdbEnabled !== false);
  const [token, setToken] = useState('');
  const [language, setLanguage] = useState(
    settings.tmdbLanguage || TMDB_LANGUAGE_BY_LOCALE[getLocale()] || 'en-US');

  useEffect(() => {
    setDirectories(settings.videoDirectories || []);
  }, [settings.videoDirectories]);
  useEffect(() => {
    setTmdbEnabled(settings.tmdbEnabled !== false);
  }, [settings.tmdbEnabled]);
  useEffect(() => {
    if (settings.tmdbLanguage) setLanguage(settings.tmdbLanguage);
  }, [settings.tmdbLanguage]);

  const current = settings.videoDirectories || [];
  const dirsDirty = directories.length !== current.length
    || directories.some((d, i) => d !== current[i]);

  return html`
    <div class="app-settings">
      <${FolderPickerField}
        label=${t('settings_app.video_directories_label')}
        hint=${t('settings_app.video_directories_hint')}
        roots=${roots} dirs=${dirs} mode="multi"
        value=${directories} disabled=${busy}
        onChange=${setDirectories} />

      <button class="btn btn-small btn-secondary" style="margin-top:4px"
        disabled=${busy || !dirsDirty}
        onClick=${() => run(() => saveDirectories(directories))}>
        ${busy ? t('settings_app.saving') : t('settings_app.save')}
      </button>

      <h4 class="app-settings-sub">${t('settings_node.tmdb_title')}</h4>
      <p class="settings-hint">${t('settings_node.tmdb_hint')}</p>

      <div class="settings-row">
        <${ToggleSwitch} checked=${tmdbEnabled} disabled=${busy}
          onChange=${(v) => { setTmdbEnabled(v);
                              run(() => transport.setTmdbEnabled(v, signFn)); }}
          label=${tmdbEnabled ? t('settings_node.tmdb_enabled')
                              : t('settings_node.tmdb_disabled')} />
      </div>

      <div class="settings-row">
        <label class="settings-label">
          ${t('settings_node.tmdb_token_label')}
          <input type="password" value=${token} disabled=${busy}
            placeholder=${t('settings_node.tmdb_token_placeholder')}
            onInput=${(e) => setToken(e.target.value)} />
        </label>
        ${/* No "optional", and no mention of a shipped default. A key that
              works without one is a key somebody else is paying the rate
              limit for, and the operator should know they are meant to have
              their own. */''}
        <p class="settings-hint">
          ${settings.tmdbTokenCustomized
            ? t('settings_node.tmdb_token_customized')
            : t('settings_app.tmdb_token_prompt')}
          ${' '}
          <a href="https://www.themoviedb.org/settings/api" target="_blank"
             rel="noopener noreferrer">${t('settings_app.tmdb_token_link')}</a>
        </p>
      </div>

      <div class="settings-row">
        <label class="settings-label">
          ${t('settings_node.tmdb_language_label')}
          <select value=${language} disabled=${busy}
            onChange=${(e) => setLanguage(e.target.value)}>
            ${LOCALES.map((l) => html`
              <option key=${l.code} value=${TMDB_LANGUAGE_BY_LOCALE[l.code]}>
                ${l.name}
              </option>`)}
          </select>
        </label>
        <p class="settings-hint">${t('settings_node.tmdb_language_hint')}</p>
      </div>

      <button class="btn btn-small btn-secondary" disabled=${busy}
        onClick=${() => run(async () => {
          // `undefined` for the token means "leave the stored one alone",
          // which is not the same as `''` — that clears it. The input starts
          // empty on every render because a secret is not read back, so
          // sending it as a value would wipe the key every time the language
          // was changed.
          await transport.setTmdbConfig(token || undefined, language, signFn);
          setToken('');
        })}>
        ${busy ? t('settings_app.saving') : t('settings_node.tmdb_save')}
      </button>
      ${msg && html`<p class="settings-hint">${msg}</p>`}
    </div>
  `;
}

export { VideoSettings, TMDB_LANGUAGE_BY_LOCALE };