@sequencemedia/music-library 1.0.0 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npm run pre-commit
package/README.md CHANGED
@@ -1,19 +1,22 @@
1
- # @sequencemedia/itunes-library
1
+ # @sequencemedia/music-library
2
2
 
3
- Parses an `iTunes Library.xml` file and transforms it to [`m3u`](https://en.wikipedia.org/wiki/M3U) files, JSON, or JavaScript.
3
+ Parses an Apple Music `Library.xml` file and transforms it to [`m3u`](https://en.wikipedia.org/wiki/M3U) files, JSON, JavaScript, or EcmaScript.
4
4
 
5
- The command line app can watch for changes to an `iTunes Library.xml` file and write `m3u` files to a destination directory.
5
+ The command line app can watch for changes to an Apple Music `Library.xml` file and write `m3u` files to a destination directory.
6
6
 
7
7
  Or, the component functions can be imported into your own application.
8
8
 
9
- This packages implements [`@sequencemedia/itunes-library-parser`](https://github.com/sequencemedia/itunes-library-parser).
9
+ This packages implements [`@sequencemedia/music-library-parser`](https://github.com/sequencemedia/music-library-parser).
10
10
 
11
11
  Requires [Java](https://www.oracle.com/java/technologies/javase-downloads.html) and [Saxon PE](https://www.saxonica.com/welcome/welcome.xml).
12
12
 
13
13
  ## Command line app
14
14
 
15
15
  ```
16
- npm run start -- --jar "/usr/local/bin/saxon/SaxonPE10-0J/saxon-pe-10.0.jar" --xml "~/Music/iTunes/iTunes Library.xml" --destination "~/Documents/iTunes Library"
16
+ npm run start -- \
17
+ --jar "/usr/local/bin/saxon/SaxonPE10-6J/saxon-pe-10.6.jar" \
18
+ --xml "~/Music/Music/Library.xml" \
19
+ --destination "~/Documents/Music Library"
17
20
  ```
18
21
 
19
22
  Paths will differ on your device.
@@ -23,12 +26,12 @@ Paths will differ on your device.
23
26
  Transforms the entire library.
24
27
 
25
28
  ```javascript
26
- const { toM3U } = require('./lib/library')
27
- const {
29
+ import { toM3U } from './src/library/index.mjs'
30
+ import {
28
31
  toJSON,
29
32
  toJS,
30
33
  toES
31
- } = require('./lib/library/transform')
34
+ } from './src/library/transform/index.mjs'
32
35
  ```
33
36
 
34
37
  ### `toM3U`
@@ -36,7 +39,7 @@ const {
36
39
  Requires the arguments `jar`, `xml`, and `destination`.
37
40
 
38
41
  - `jar` - the path to the Saxon binary on your device
39
- - `xml` - the path to the `iTunes Library.xml` file
42
+ - `xml` - the path to the Apple Music `Library.xml` file
40
43
  - `destination` - the path for the `m3u` files to be written
41
44
 
42
45
  Returns a `Promise` resolving when all `m3u` files are written.
@@ -46,7 +49,7 @@ Returns a `Promise` resolving when all `m3u` files are written.
46
49
  Requires the arguments `jar`, and `xml`.
47
50
 
48
51
  - `jar` - the path to the Saxon binary on your device
49
- - `xml` - the path to the `iTunes Library.xml` file
52
+ - `xml` - the path to the Apple Music `Library.xml` file
50
53
 
51
54
  Returns a `Promise` resolving to a `JSON` string.
52
55
 
@@ -55,7 +58,7 @@ Returns a `Promise` resolving to a `JSON` string.
55
58
  Requires the arguments `jar`, and `xml`.
56
59
 
57
60
  - `jar` - the path to the Saxon binary on your device
58
- - `xml` - the path to the `iTunes Library.xml` file
61
+ - `xml` - the path to the Apple Music `Library.xml` file
59
62
 
60
63
  Returns a `Promise` resolving to a JavaScript object.
61
64
 
@@ -64,7 +67,7 @@ Returns a `Promise` resolving to a JavaScript object.
64
67
  Requires the arguments `jar`, and `xml`.
65
68
 
66
69
  - `jar` - the path to the Saxon binary on your device
67
- - `xml` - the path to the `iTunes Library.xml` file
70
+ - `xml` - the path to the Apple Music `Library.xml` file
68
71
 
69
72
  Returns a `Promise` resolving to a collection of JavaScript `Map` and `Set` instances.
70
73
 
@@ -73,12 +76,12 @@ Returns a `Promise` resolving to a collection of JavaScript `Map` and `Set` inst
73
76
  Transforms the playlists.
74
77
 
75
78
  ```javascript
76
- const { toM3U } = require('./lib/library/playlists')
77
- const {
79
+ import { toM3U } from './src/library/playlists/index.mjs'
80
+ import {
78
81
  toJSON,
79
82
  toJS,
80
83
  toES
81
- } = require('./lib/library/playlists/transform')
84
+ } from './src/library/playlists/transform/index.mjs'
82
85
  ```
83
86
 
84
87
  See **Library**.
@@ -88,12 +91,12 @@ See **Library**.
88
91
  Transforms the tracks.
89
92
 
90
93
  ```javascript
91
- const { toM3U } = require('./lib/library/tracks')
92
- const {
94
+ import { toM3U } from './src/library/tracks/index.mjs'
95
+ import {
93
96
  toJSON,
94
97
  toJS,
95
98
  toES
96
- } = require('./lib/library/tracks/transform')
99
+ } from './src/library/tracks/transform/index.mjs'
97
100
  ```
98
101
 
99
102
  See **Library**.
package/app.mjs CHANGED
@@ -1,133 +1,149 @@
1
- #!/usr/bin/env node
2
-
3
- import 'dotenv/config'
4
-
5
- import debug from 'debug'
6
-
7
- import {
8
- readFile
9
- } from 'fs/promises'
10
-
11
- import psList from 'ps-list'
12
-
13
- import commander from 'commander'
14
-
15
- import * as watch from '#music-library/watch'
16
-
17
- const {
18
- library,
19
- library: {
20
- tracks,
21
- playlists
22
- }
23
- } = watch
24
-
25
- const NAME = 'ml.App'
26
- process.title = NAME
27
-
28
- async function app () {
29
- const PACKAGE = JSON.parse(await readFile('./package.json'))
30
-
31
- const {
32
- name
33
- } = PACKAGE
34
-
35
- /**
36
- * Permit only one instance of the application
37
- */
38
- try {
39
- const a = (await psList())
40
- .filter(({ name }) => name === NAME)
41
-
42
- if (a.length > 1) {
43
- const {
44
- pid: PID
45
- } = process
46
-
47
- const {
48
- pid
49
- } = a.find(({ pid }) => pid !== PID)
50
-
51
- const log = debug('music-library:process:log')
52
-
53
- log(`Killing application "${name}" in process ${pid}.`)
54
-
55
- process.kill(pid)
56
- }
57
- } catch ({ message }) {
58
- const error = debug('music-library:process:error')
59
-
60
- error(message)
61
- return
62
- }
63
-
64
- const log = debug('@sequencemedia/music-library')
65
-
66
- const {
67
- pid,
68
- argv,
69
- env: {
70
- JAR,
71
- XML,
72
- DESTINATION
73
- }
74
- } = process
75
-
76
- log(`Starting application "${name}" in process ${pid}.`)
77
-
78
- const {
79
- version
80
- } = PACKAGE
81
-
82
- commander
83
- .version(version)
84
- .option('-j, --jar [jar]', 'Path to Saxon PE/EE JAR')
85
- .option('-x, --xml [xml]', 'Path to iTunes Library XML')
86
- .option('-d, --destination [destination]', 'Destination path for M3Us')
87
- .option('-t, --tracks', 'Parse all tracks')
88
- .option('-p, --playlists', 'Parse all playlists')
89
- .parse(argv)
90
-
91
- const {
92
- jar = JAR,
93
- xml = XML,
94
- destination = DESTINATION,
95
- tracks: t = false,
96
- playlists: p = false
97
- } = commander.opts()
98
-
99
- const l = (
100
- (t && p) || (!t && !p)
101
- )
102
-
103
- if (l) {
104
- log(`Application "${name}" in process ${pid} watching Library.`)
105
-
106
- log({ jar, xml, destination })
107
-
108
- return (
109
- library
110
- .toM3U(jar, xml, destination)
111
- )
112
- } else {
113
- if (t) {
114
- log(`Application "${name}" in process ${pid} watching Tracks.`)
115
-
116
- return (
117
- tracks
118
- .toM3U(jar, xml, destination)
119
- )
120
- }
121
-
122
- if (p) {
123
- log(`Application "${name}" in process ${pid} watching Playlists.`)
124
-
125
- return (
126
- playlists
127
- .toM3U(jar, xml, destination)
128
- )
129
- }
130
- }
131
- }
132
-
133
- export default app()
1
+ #!/usr/bin/env node
2
+
3
+ import 'dotenv/config'
4
+
5
+ import debug from 'debug'
6
+
7
+ import {
8
+ readFile
9
+ } from 'fs/promises'
10
+
11
+ import psList from 'ps-list'
12
+
13
+ import commander from 'commander'
14
+
15
+ import * as watch from '#music-library/watch'
16
+
17
+ const {
18
+ library,
19
+ library: {
20
+ tracks,
21
+ playlists
22
+ }
23
+ } = watch
24
+
25
+ const log = debug('@sequencemedia/music-library')
26
+
27
+ log('`music-library` is awake')
28
+
29
+ const NAME = 'ml.App'
30
+ process.title = NAME
31
+
32
+ async function app () {
33
+ const PACKAGE = JSON.parse(await readFile('./package.json'))
34
+
35
+ const {
36
+ name
37
+ } = PACKAGE
38
+
39
+ /**
40
+ * Permit only one instance of the application
41
+ */
42
+ try {
43
+ const a = (await psList())
44
+ .filter(({ name }) => name === NAME)
45
+
46
+ if (a.length > 1) {
47
+ const {
48
+ pid: PID
49
+ } = process
50
+
51
+ const {
52
+ pid
53
+ } = a.find(({ pid }) => pid !== PID)
54
+
55
+ const log = debug('@sequencemedia/music-library:process:log')
56
+
57
+ log(`Killing application "${name}" in process ${pid}.`)
58
+
59
+ process.kill(pid)
60
+ }
61
+ } catch ({ message }) {
62
+ const error = debug('@sequencemedia/music-library:process:error')
63
+
64
+ error(message)
65
+ return
66
+ }
67
+
68
+ const {
69
+ pid,
70
+ argv,
71
+ env: {
72
+ TRACKS = false,
73
+ PLAYLISTS = false
74
+ }
75
+ } = process
76
+
77
+ log(`Starting application "${name}" in process ${pid}.`)
78
+
79
+ const {
80
+ version
81
+ } = PACKAGE
82
+
83
+ try {
84
+ commander
85
+ .version(version)
86
+ .exitOverride()
87
+ .requiredOption('-j, --jar [jar]', 'Path to Saxon PE/EE JAR')
88
+ .requiredOption('-x, --xml [xml]', 'Path to iTunes Library XML')
89
+ .requiredOption('-d, --destination [destination]', 'Destination path for M3Us')
90
+ .option('-t, --tracks', 'Parse all tracks')
91
+ .option('-p, --playlists', 'Parse all playlists')
92
+ .parse(argv)
93
+ } catch (e) {
94
+ const {
95
+ code
96
+ } = e
97
+
98
+ const error = debug('@sequencemedia/music-library:commander:error')
99
+
100
+ if (code !== 'commander.missingMandatoryOptionValue') error(e)
101
+
102
+ error(`Halting application "${name}" in process ${pid}.`)
103
+ return
104
+ }
105
+
106
+ const {
107
+ jar,
108
+ xml,
109
+ destination,
110
+ tracks: t = TRACKS,
111
+ playlists: p = PLAYLISTS
112
+ } = commander.opts()
113
+
114
+ log({
115
+ jar,
116
+ xml,
117
+ destination
118
+ })
119
+
120
+ try {
121
+ const l = (
122
+ (t && p) || (!t && !p)
123
+ )
124
+
125
+ if (l) {
126
+ log(`Application "${name}" in process ${pid} watching Library.`)
127
+
128
+ await library.toM3U(jar, xml, destination)
129
+ } else {
130
+ if (t) {
131
+ log(`Application "${name}" in process ${pid} watching Tracks.`)
132
+
133
+ await tracks.toM3U(jar, xml, destination)
134
+ } else {
135
+ if (p) {
136
+ log(`Application "${name}" in process ${pid} watching Playlists.`)
137
+
138
+ await playlists.toM3U(jar, xml, destination)
139
+ }
140
+ }
141
+ }
142
+ } catch ({ message }) {
143
+ const error = debug('@sequencemedia/music-library:error')
144
+
145
+ error(message)
146
+ }
147
+ }
148
+
149
+ export default app()
package/babel.config.cjs CHANGED
@@ -1,24 +1,24 @@
1
- const presets = [
2
- [
3
- '@babel/env',
4
- {
5
- targets: {
6
- node: 'current'
7
- },
8
- useBuiltIns: 'usage',
9
- corejs: 3
10
- }
11
- ]
12
- ]
13
-
14
- const plugins = [
15
- '@babel/proposal-export-default-from',
16
- '@babel/proposal-export-namespace-from'
17
- ]
18
-
19
- module.exports = {
20
- compact: true,
21
- comments: false,
22
- presets,
23
- plugins
24
- }
1
+ const presets = [
2
+ [
3
+ '@babel/env',
4
+ {
5
+ targets: {
6
+ node: 'current'
7
+ },
8
+ useBuiltIns: 'usage',
9
+ corejs: 3
10
+ }
11
+ ]
12
+ ]
13
+
14
+ const plugins = [
15
+ '@babel/proposal-export-default-from',
16
+ '@babel/proposal-export-namespace-from'
17
+ ]
18
+
19
+ module.exports = {
20
+ compact: true,
21
+ comments: false,
22
+ presets,
23
+ plugins
24
+ }
@@ -0,0 +1,114 @@
1
+ import debug from 'debug'
2
+
3
+ import {
4
+ exec
5
+ } from 'child_process'
6
+
7
+ const log = debug('@sequencemedia/music-library:build:gulp:pre-commit')
8
+
9
+ log('`@sequencemedia/music-library` is awake')
10
+
11
+ const PACKAGE_VERSION_CHANGES = /\-+\s+"version":\s+"(\d+\.\d+\.\d+)",+\s+\++\s+"version":\s+"(\d+\.\d+\.\d+)",+\s+/ /* eslint-disable-line no-useless-escape */
12
+
13
+ const HAS_STAGED_CHANGES = /Changes to be committed/s
14
+
15
+ const NOT_STAGED_CHANGES = /Changes not staged for commit/s
16
+
17
+ const OPTIONS = {
18
+ maxBuffer: 1024 * 500
19
+ }
20
+
21
+ export function hasPackageVersionChanges () {
22
+ log('hasPackageVersionChanges')
23
+
24
+ return (
25
+ new Promise((resolve, reject) => {
26
+ exec('git diff HEAD origin/main package.json', OPTIONS, (e, v) => (!e) ? resolve(PACKAGE_VERSION_CHANGES.test(v)) : reject(e))
27
+ })
28
+ )
29
+ }
30
+
31
+ export function notPackageVersionChanges () {
32
+ log('notPackageVersionChanges')
33
+
34
+ return (
35
+ new Promise((resolve, reject) => {
36
+ exec('git diff HEAD origin/main package.json', OPTIONS, (e, v) => (!e) ? resolve(PACKAGE_VERSION_CHANGES.test(v) !== true) : reject(e))
37
+ })
38
+ )
39
+ }
40
+
41
+ export function hasStagedChanges () {
42
+ log('hasStagedChanges')
43
+
44
+ return (
45
+ new Promise((resolve, reject) => {
46
+ exec('git status', OPTIONS, (e, v) => (!e) ? resolve(HAS_STAGED_CHANGES.test(v)) : reject(e))
47
+ })
48
+ )
49
+ }
50
+
51
+ export function notStagedChanges () {
52
+ log('notStagedChanges')
53
+
54
+ return (
55
+ new Promise((resolve, reject) => {
56
+ exec('git status', OPTIONS, (e, v) => (!e) ? resolve(NOT_STAGED_CHANGES.test(v)) : reject(e))
57
+ })
58
+ )
59
+ }
60
+
61
+ export function notPushedChanges () {
62
+ log('notPushedChanges')
63
+
64
+ return (
65
+ new Promise((resolve, reject) => {
66
+ exec('git log origin/main..HEAD', OPTIONS, (e, v) => (!e) ? resolve(!!v) : reject(e))
67
+ })
68
+ )
69
+ }
70
+
71
+ export function patchPackageVersion () {
72
+ log('patchPackageVersion')
73
+
74
+ return (
75
+ new Promise((resolve, reject) => {
76
+ exec('npm version patch -m %s -n --no-git-tag-version --no-verify', OPTIONS, (e) => (!e) ? resolve() : reject(e))
77
+ })
78
+ )
79
+ }
80
+
81
+ export function addPackageVersionChanges () {
82
+ log('addPackageVersionChanges')
83
+
84
+ return (
85
+ new Promise((resolve, reject) => {
86
+ exec('git add package.json package-lock.json', OPTIONS, (e) => (!e) ? resolve() : reject(e))
87
+ })
88
+ )
89
+ }
90
+
91
+ export default async function preCommit () {
92
+ log('pre-commit')
93
+
94
+ try {
95
+ /**
96
+ * Not changes added, exit
97
+ */
98
+ if (await notStagedChanges()) return
99
+ /**
100
+ * Has changes added, continue
101
+ */
102
+ if (await hasStagedChanges()) {
103
+ /**
104
+ * Not package version changes, continue
105
+ */
106
+ if (await notPackageVersionChanges()) {
107
+ await patchPackageVersion()
108
+ await addPackageVersionChanges()
109
+ }
110
+ }
111
+ } catch ({ code = 'NONE', message = 'No error message defined' }) {
112
+ log({ code, message })
113
+ }
114
+ }
package/gulpfile.mjs ADDED
@@ -0,0 +1,9 @@
1
+ import gulp from 'gulp'
2
+
3
+ import preCommit from '#build/gulp/pre-commit'
4
+
5
+ gulp
6
+ .task('pre-commit', preCommit)
7
+
8
+ gulp
9
+ .task('default', gulp.series('pre-commit'))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sequencemedia/music-library",
3
- "version": "1.0.0",
3
+ "version": "1.0.4",
4
4
  "description": "Watch Apple Music Library XML and parse to JSON, JS, ES, or M3U",
5
5
  "keywords": [
6
6
  "music",
@@ -34,17 +34,20 @@
34
34
  "scripts": {
35
35
  "lint": "eslint . --ext .mjs --ext .cjs",
36
36
  "lint:fix": "npm run lint -- --fix",
37
- "start": "cross-env DEBUG=@sequencemedia/music-library* NODE_ENV=production node .",
38
- "start:dev": "cross-env DEBUG=@sequencemedia/music-library* NODE_ENV=development node ."
37
+ "start": "cross-env DEBUG=@sequencemedia/music-library,@sequencemedia/music-library:*,@sequencemedia/music-library-parser,@sequencemedia/music-library-parser:* NODE_ENV=production node .",
38
+ "dev": "cross-env DEBUG=@sequencemedia/music-library,@sequencemedia/music-library:*,@sequencemedia/music-library-parser,@sequencemedia/music-library-parser:* NODE_ENV=development node .",
39
+ "gulp": "gulp --color",
40
+ "pre-commit": "npm run lint && cross-env DEBUG=@sequencemedia/music-library,@sequencemedia/music-library:* npm run gulp -- pre-commit",
41
+ "prepare": "husky install"
39
42
  },
40
43
  "dependencies": {
41
- "@sequencemedia/music-library-parser": "1.0.0",
44
+ "@sequencemedia/music-library-parser": "1.0.2",
42
45
  "chokidar": "^3.5.2",
43
46
  "commander": "^8.3.0",
44
47
  "cross-env": "^7.0.3",
45
48
  "debug": "^4.3.3",
46
49
  "dotenv": "^10.0.0",
47
- "ps-list": "7.2.0",
50
+ "ps-list": "^8.0.0",
48
51
  "supports-color": "^9.2.1"
49
52
  },
50
53
  "devDependencies": {
@@ -57,21 +60,27 @@
57
60
  "core-js": "^3.20.1",
58
61
  "eslint": "7.32.0",
59
62
  "eslint-config-standard": "^16.0.3",
60
- "eslint-import-resolver-babel-module": "^5.3.1",
61
63
  "eslint-plugin-import": "^2.25.3",
62
64
  "eslint-plugin-node": "^11.1.0",
63
65
  "eslint-plugin-promise": "5.2.0",
64
- "rimraf": "^3.0.2"
66
+ "gulp": "^4.0.2",
67
+ "husky": "^7.0.4"
65
68
  },
66
69
  "imports": {
67
70
  "#music-library/common/normalise": "./src/common/normalise.mjs",
68
71
  "#music-library/library": "./src/library/index.mjs",
72
+ "#music-library/library/transform": "./src/library/transform/index.mjs",
73
+ "#music-library/library/playlists": "./src/library/playlists/index.mjs",
74
+ "#music-library/library/playlists/transform": "./src/library/playlists/transform/index.mjs",
75
+ "#music-library/library/tracks": "./src/library/tracks/index.mjs",
76
+ "#music-library/library/tracks/transform": "./src/library/tracks/transform/index.mjs",
69
77
  "#music-library/watch": "./src/watch/index.mjs",
70
78
  "#music-library/watch/library": "./src/watch/library/index.mjs",
71
79
  "#music-library/watch/library/transform": "./src/watch/library/transform/index.mjs",
72
80
  "#music-library/watch/library/playlists": "./src/watch/library/playlists/index.mjs",
73
81
  "#music-library/watch/library/playlists/transform": "./src/watch/library/playlists/transform/index.mjs",
74
82
  "#music-library/watch/library/tracks": "./src/watch/library/tracks/index.mjs",
75
- "#music-library/watch/library/tracks/transform": "./src/watch/library/tracks/transform/index.mjs"
83
+ "#music-library/watch/library/tracks/transform": "./src/watch/library/tracks/transform/index.mjs",
84
+ "#build/gulp/*": "./build/gulp/*.mjs"
76
85
  }
77
86
  }
@@ -2,8 +2,10 @@ import {
2
2
  homedir
3
3
  } from 'os'
4
4
 
5
- import path from 'path'
5
+ import {
6
+ resolve
7
+ } from 'path'
6
8
 
7
- const normalise = (p) => path.resolve(p.trim().replace(/^~/, homedir()))
9
+ const normalise = (p = '') => resolve(String(p).trim().replace(/^~/, homedir()))
8
10
 
9
11
  export default normalise
package/src/index.mjs CHANGED
@@ -1 +1 @@
1
- export * as library from '#music-library/library'
1
+ export * as library from '#music-library/library'