Skip to content

Commit

Permalink
feat: support profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
jordanbtucker committed Oct 15, 2022
1 parent 3c70cfb commit f2b6596
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 33 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Features

- Profiles are available via the `--profile` command line argument. If omitted,
the default profile will be used, which is the same as using
`--profile default`.

## 2.1.0 - 2022-09-15

### Features
Expand Down
73 changes: 63 additions & 10 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,37 @@ const {default: fetch} = require('cross-fetch')
const Conf = require('conf')
const pkg = require('../package.json')

/**
* @typedef Configuration
* @property {2} version The version of the configuration
* @property {Record<string, ConfigurationProfile>} profiles The configuration profiles
*/

/**
* @typedef ConfigurationProfile
* @property {string} tmdbAccessToken The Movie Database access token
* @property {string} inputDir The input directory
* @property {string} moviesDir The movies directory
* @property {string} showsDir The TV shows directory
* @property {boolean} renameMK3DToMKV Whether to rename *.mk3d files to *.mkv
*/

/** @type {import('conf').default<Configuration>} */
let conf
/** @type {string} */
let profileName = 'default'
/** @type {ConfigurationProfile} */
let profile
let _tmdbCache
/** @type {string} */
let inputDir
/** @type {string} */
let moviesDir
/** @type {string} */
let showsDir
/** @type {string} */
let tmdbAccessToken
/** @type {boolean} */
let renameMK3DToMKV

/**
Expand Down Expand Up @@ -350,9 +375,11 @@ async function main() {
async function ensureConf() {
conf = new Conf()

migrateConf()

// An API Read Access Token (v4 auth) is required to access TMDb API.
if (!tmdbAccessToken) {
tmdbAccessToken = conf.get('tmdbAccessToken')
tmdbAccessToken = conf.get(`profiles.${profileName}.tmdbAccessToken`)
}
if (!tmdbAccessToken) {
tmdbAccessToken = await promptInput(
Expand All @@ -365,12 +392,12 @@ async function ensureConf() {
throw err
}

conf.set('tmdbAccessToken', tmdbAccessToken)
conf.set(`profiles.${profileName}.tmdbAccessToken`, tmdbAccessToken)
}

// The input directory is where this application looks for new video files.
if (!inputDir) {
inputDir = conf.get('inputDir')
inputDir = conf.get(`profiles.${profileName}.inputDir`)
}
if (!inputDir) {
inputDir = await promptInput(
Expand All @@ -382,12 +409,12 @@ async function ensureConf() {
throw err
}

conf.set('inputDir', inputDir)
conf.set(`profiles.${profileName}.inputDir`, inputDir)
}

// The movies directory is where the application will put renamed movie files.
if (!moviesDir) {
moviesDir = conf.get('moviesDir')
moviesDir = conf.get(`profiles.${profileName}.moviesDir`)
}
if (!moviesDir) {
moviesDir = await promptInput(
Expand All @@ -399,12 +426,12 @@ async function ensureConf() {
throw err
}

conf.set('moviesDir', moviesDir)
conf.set(`profiles.${profileName}.moviesDir`, moviesDir)
}

// The TV shows directory is where this application will put renamed TV show files.
if (!showsDir) {
showsDir = conf.get('showsDir')
showsDir = conf.get(`profiles.${profileName}.showsDir`)
}
if (!showsDir) {
showsDir = await promptInput(
Expand All @@ -416,13 +443,13 @@ async function ensureConf() {
throw err
}

conf.set('showsDir', showsDir)
conf.set(`profiles.${profileName}.showsDir`, showsDir)
}

// The rename MK3D to MKV setting changes the file extension of *.mk3d file to *.mkv.
// Plex does not support the *.mk3d file extension, so this is a workaround.
if (renameMK3DToMKV == null) {
renameMK3DToMKV = conf.get('renameMK3DToMKV')
renameMK3DToMKV = conf.get(`profiles.${profileName}.renameMK3DToMKV`)
}
if (renameMK3DToMKV == null) {
renameMK3DToMKV = await promptConfirm(
Expand All @@ -434,7 +461,29 @@ async function ensureConf() {
throw err
}

conf.set('renameMK3DToMKV', renameMK3DToMKV)
conf.set(`profiles.${profileName}.renameMK3DToMKV`, renameMK3DToMKV)
}
}

/**
* Migrates the configuration to the latest version.
*/
function migrateConf() {
// Version 1 configurations do not have a version number.
if (conf.get('version') == null) {
// Move top level configuration values into a profile.
profile = {
tmdbAccessToken: conf.get('tmdbAccessToken'),
inputDir: conf.get('inputDir'),
moviesDir: conf.get('moviesDir'),
showsDir: conf.get('showsDir'),
renameMK3DToMKV: conf.get('renameMK3DToMKV'),
}

// Clear the configuration, set the version, and set the default profile.
conf.clear()
conf.set('version', 2)
conf.set('profiles', {default: profile})
}
}

Expand All @@ -451,6 +500,9 @@ function handleArgs() {
// eslint-disable-next-line no-process-exit
process.exit()
break
case '--profile':
profileName = args[++i]
break
case '--token':
tmdbAccessToken = args[++i]
break
Expand Down Expand Up @@ -482,6 +534,7 @@ Usage: ${pkg.name} [options]
Options:
--help Displays this message
--profile The profile to use, otherwise uses the default profile
--token The Movie Database access token
--input The input folder
--movies The movies folder
Expand Down
127 changes: 104 additions & 23 deletions test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const {createMockCrossFetchPackage} = require('./mock-cross-fetch-package')
const {createMockFSPackage} = require('./mock-fs-package')
const {createMockInquirerPackage} = require('./mock-inquirer-package')
const {
clone,
TEST_PROFILE_NAME,
TEST_TMDB_ACCESS_TOKEN,
TEST_TMDB_ACCESS_TOKEN_ANSWER,
TEST_TMDB_MOVIE,
Expand Down Expand Up @@ -42,16 +42,19 @@ const {
TEST_SKIPPING_ANSWER,
} = require('./util')

const DEFAULT_PROFILE_NAME = 'default'

/** @type {import('./mock-conf-package').MockConfPackageState} */
const DEFAULT_MOCK_CONF_PACKAGE_STATE = {
const DEFAULT_MOCK_CONF_PACKAGE_STATE = (profile = DEFAULT_PROFILE_NAME) => ({
values: {
tmdbAccessToken: TEST_TMDB_ACCESS_TOKEN,
inputDir: TEST_INPUT_DIR,
moviesDir: TEST_MOVIES_DIR,
showsDir: TEST_SHOWS_DIR,
renameMK3DToMKV: true,
version: 2,
[`profiles.${profile}.tmdbAccessToken`]: TEST_TMDB_ACCESS_TOKEN,
[`profiles.${profile}.inputDir`]: TEST_INPUT_DIR,
[`profiles.${profile}.moviesDir`]: TEST_MOVIES_DIR,
[`profiles.${profile}.showsDir`]: TEST_SHOWS_DIR,
[`profiles.${profile}.renameMK3DToMKV`]: true,
},
}
})

/** @type {import('./mock-cross-fetch-package').MockCrossFetchPackageState} */
const DEFAULT_MOCK_CROSS_FETCH_PACKAGE_STATE = {
Expand All @@ -78,7 +81,7 @@ function requireMockMainModule({
} = {}) {
const mockFSPackage = createMockFSPackage(fsPackageState || {})
const mockConfPackage = createMockConfPackage(
confPackageState || clone(DEFAULT_MOCK_CONF_PACKAGE_STATE),
confPackageState || DEFAULT_MOCK_CONF_PACKAGE_STATE(),
)
const mockCrossFetchPackage = createMockCrossFetchPackage(
crossFetchPackageState || DEFAULT_MOCK_CROSS_FETCH_PACKAGE_STATE,
Expand Down Expand Up @@ -127,11 +130,13 @@ t.test('conf', async t => {
t.strictSame(
mockConfPackageState.values,
{
tmdbAccessToken: TEST_TMDB_ACCESS_TOKEN,
inputDir: TEST_INPUT_DIR,
moviesDir: TEST_MOVIES_DIR,
showsDir: TEST_SHOWS_DIR,
renameMK3DToMKV: true,
version: 2,
[`profiles.${DEFAULT_PROFILE_NAME}.tmdbAccessToken`]:
TEST_TMDB_ACCESS_TOKEN,
[`profiles.${DEFAULT_PROFILE_NAME}.inputDir`]: TEST_INPUT_DIR,
[`profiles.${DEFAULT_PROFILE_NAME}.moviesDir`]: TEST_MOVIES_DIR,
[`profiles.${DEFAULT_PROFILE_NAME}.showsDir`]: TEST_SHOWS_DIR,
[`profiles.${DEFAULT_PROFILE_NAME}.renameMK3DToMKV`]: true,
},
'conf matches',
)
Expand All @@ -144,7 +149,7 @@ t.test('conf', async t => {
},
}

const mockConfPackageState = {...DEFAULT_MOCK_CONF_PACKAGE_STATE}
const mockConfPackageState = DEFAULT_MOCK_CONF_PACKAGE_STATE()

const {main} = requireMockMainModule({
fsPackageState: mockFSPackageState,
Expand All @@ -159,11 +164,13 @@ t.test('conf', async t => {
t.strictSame(
mockConfPackageState.values,
{
tmdbAccessToken: TEST_TMDB_ACCESS_TOKEN,
inputDir: TEST_INPUT_DIR,
moviesDir: TEST_MOVIES_DIR,
showsDir: TEST_SHOWS_DIR,
renameMK3DToMKV: true,
version: 2,
[`profiles.${DEFAULT_PROFILE_NAME}.tmdbAccessToken`]:
TEST_TMDB_ACCESS_TOKEN,
[`profiles.${DEFAULT_PROFILE_NAME}.inputDir`]: TEST_INPUT_DIR,
[`profiles.${DEFAULT_PROFILE_NAME}.moviesDir`]: TEST_MOVIES_DIR,
[`profiles.${DEFAULT_PROFILE_NAME}.showsDir`]: TEST_SHOWS_DIR,
[`profiles.${DEFAULT_PROFILE_NAME}.renameMK3DToMKV`]: true,
},
'conf matches',
)
Expand Down Expand Up @@ -318,6 +325,40 @@ t.test('conf', async t => {

process.exitCode = 0
})

t.test('migrates', async t => {
const mockFSPackageState = {
entries: {
[TEST_INPUT_DIR]: 'directory',
},
}

const mockConfPackageState = {
values: {
tmdbAccessToken: TEST_TMDB_ACCESS_TOKEN,
inputDir: TEST_INPUT_DIR,
moviesDir: TEST_MOVIES_DIR,
showsDir: TEST_SHOWS_DIR,
renameMK3DToMKV: true,
},
}

const {main} = requireMockMainModule({
fsPackageState: mockFSPackageState,
confPackageState: mockConfPackageState,
inquirerPackageState: {
answers: [TEST_MODE_ANSWER('movies'), TEST_FILES_ANSWER([])],
},
})

await main()

t.strictSame(
mockConfPackageState.values,
DEFAULT_MOCK_CONF_PACKAGE_STATE().values,
'conf matches',
)
})
})

t.test('args', async t => {
Expand Down Expand Up @@ -355,7 +396,7 @@ t.test('args', async t => {

await main()

t.strictSame(mockConfPackageState.values, {}, 'conf matches')
t.strictSame(mockConfPackageState.values, {version: 2}, 'conf matches')

process.argv = argv
})
Expand Down Expand Up @@ -390,6 +431,46 @@ t.test('args', async t => {
process.argv = argv
})

t.test('uses profile', async t => {
const mockFSPackageState = {
entries: {
[TEST_INPUT_DIR]: 'directory',
},
}

const mockConfPackageState =
DEFAULT_MOCK_CONF_PACKAGE_STATE(TEST_PROFILE_NAME)

const {main} = requireMockMainModule({
fsPackageState: mockFSPackageState,
confPackageState: mockConfPackageState,
inquirerPackageState: {
answers: [TEST_MODE_ANSWER('movies'), TEST_FILES_ANSWER([])],
},
})

const argv = process.argv
process.argv = [argv[0], argv[1], '--profile', TEST_PROFILE_NAME]

await main()

t.strictSame(
mockConfPackageState.values,
{
version: 2,
[`profiles.${TEST_PROFILE_NAME}.tmdbAccessToken`]:
TEST_TMDB_ACCESS_TOKEN,
[`profiles.${TEST_PROFILE_NAME}.inputDir`]: TEST_INPUT_DIR,
[`profiles.${TEST_PROFILE_NAME}.moviesDir`]: TEST_MOVIES_DIR,
[`profiles.${TEST_PROFILE_NAME}.showsDir`]: TEST_SHOWS_DIR,
[`profiles.${TEST_PROFILE_NAME}.renameMK3DToMKV`]: true,
},
'conf matches',
)

process.argv = argv
})

t.test('throws on unknown options', async t => {
const mockFSPackageState = {
entries: {
Expand Down Expand Up @@ -1353,8 +1434,8 @@ t.test('mk3d to mkv', async t => {

const mockConfPackageState = {
values: {
...DEFAULT_MOCK_CONF_PACKAGE_STATE.values,
renameMK3DToMKV: false,
...DEFAULT_MOCK_CONF_PACKAGE_STATE(DEFAULT_PROFILE_NAME).values,
[`profiles.${DEFAULT_PROFILE_NAME}.renameMK3DToMKV`]: false,
},
}

Expand Down
2 changes: 2 additions & 0 deletions test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function pad(number) {
return String(number).padStart(2, '0')
}

const TEST_PROFILE_NAME = 'test-profile-name'
const TEST_TMDB_ACCESS_TOKEN = 'test-tmdb-access-token'
const TEST_INPUT_DIR = 'test-input-dir'
const TEST_MOVIES_DIR = 'test-movies-dir'
Expand Down Expand Up @@ -650,6 +651,7 @@ const TEST_SKIPPING_ANSWER = filename => ({
module.exports = {
clone,
immediate,
TEST_PROFILE_NAME,
TEST_TMDB_ACCESS_TOKEN,
TEST_TMDB_ACCESS_TOKEN_ANSWER,
TEST_TMDB_MOVIE,
Expand Down

0 comments on commit f2b6596

Please sign in to comment.