Merge branch 'master' into release
This commit is contained in:
commit
a66d6c38c3
58 changed files with 3009 additions and 1407 deletions
15
.eslintrc
15
.eslintrc
|
|
@ -2,14 +2,8 @@
|
||||||
"root": true,
|
"root": true,
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"env": { "node": true },
|
"env": { "node": true },
|
||||||
"plugins": [
|
"plugins": ["@typescript-eslint", "only-warn"],
|
||||||
"@typescript-eslint"
|
"extends": ["eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended"],
|
||||||
],
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"sourceType": "module"
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
|
|
@ -18,6 +12,9 @@
|
||||||
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
|
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"no-prototype-builtins": "off",
|
"no-prototype-builtins": "off",
|
||||||
"@typescript-eslint/no-empty-function": "off"
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"@typescript-eslint/no-inferrable-types": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": ["warn"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2
.github/ISSUE_TEMPLATE/api-request.md
vendored
2
.github/ISSUE_TEMPLATE/api-request.md
vendored
|
|
@ -4,7 +4,6 @@ about: Suggest a new API to be added to this plugin.
|
||||||
title: ''
|
title: ''
|
||||||
labels: API request
|
labels: API request
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Name**
|
**Name**
|
||||||
|
|
@ -16,6 +15,5 @@ A link to their API documentation
|
||||||
**What does the API do/offer**
|
**What does the API do/offer**
|
||||||
A short description of what data the API offers
|
A short description of what data the API offers
|
||||||
|
|
||||||
|
|
||||||
- [ ] Is the API free to use
|
- [ ] Is the API free to use
|
||||||
- [ ] Does the API require authentication
|
- [ ] Does the API require authentication
|
||||||
|
|
|
||||||
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -4,7 +4,6 @@ about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- [ ] The Plugin is up to date
|
- [ ] The Plugin is up to date
|
||||||
|
|
@ -15,6 +14,7 @@ A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
|
|
@ -27,6 +27,7 @@ A clear and concise description of what you expected to happen.
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Occurs on**
|
**Occurs on**
|
||||||
|
|
||||||
- [ ] Windows
|
- [ ] Windows
|
||||||
- [ ] macOS
|
- [ ] macOS
|
||||||
- [ ] Linux
|
- [ ] Linux
|
||||||
|
|
|
||||||
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -4,7 +4,6 @@ about: Suggest an idea for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: feature request
|
labels: feature request
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
|
|
||||||
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
|
|
@ -4,7 +4,7 @@ on:
|
||||||
push:
|
push:
|
||||||
# Sequence of patterns matched against refs/tags
|
# Sequence of patterns matched against refs/tags
|
||||||
tags:
|
tags:
|
||||||
- "*" # Push events to matching any tag format, i.e. 1.0, 20.15.10
|
- '*' # Push events to matching any tag format, i.e. 1.0, 20.15.10
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PLUGIN_NAME: obsidian-media-db-plugin # Change this to the name of your plugin-id folder
|
PLUGIN_NAME: obsidian-media-db-plugin # Change this to the name of your plugin-id folder
|
||||||
|
|
@ -14,11 +14,11 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: "14.x" # You might need to adjust this value to your own version
|
node-version: '16.x' # You might need to adjust this value to your own version
|
||||||
- name: Build
|
- name: Build
|
||||||
id: build
|
id: build
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -28,7 +28,8 @@ jobs:
|
||||||
cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }}
|
cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }}
|
||||||
zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
|
zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
|
||||||
ls
|
ls
|
||||||
echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)"
|
echo "tag_name=$(git tag --sort version:refname | tail -n 1)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
|
|
@ -40,6 +41,7 @@ jobs:
|
||||||
release_name: ${{ github.ref }}
|
release_name: ${{ github.ref }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
- name: Upload zip file
|
- name: Upload zip file
|
||||||
id: upload-zip
|
id: upload-zip
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
|
|
@ -50,6 +52,7 @@ jobs:
|
||||||
asset_path: ./${{ env.PLUGIN_NAME }}.zip
|
asset_path: ./${{ env.PLUGIN_NAME }}.zip
|
||||||
asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip
|
asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip
|
||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
|
|
||||||
- name: Upload main.js
|
- name: Upload main.js
|
||||||
id: upload-main
|
id: upload-main
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
|
|
@ -60,6 +63,7 @@ jobs:
|
||||||
asset_path: ./main.js
|
asset_path: ./main.js
|
||||||
asset_name: main.js
|
asset_name: main.js
|
||||||
asset_content_type: text/javascript
|
asset_content_type: text/javascript
|
||||||
|
|
||||||
- name: Upload manifest.json
|
- name: Upload manifest.json
|
||||||
id: upload-manifest
|
id: upload-manifest
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
|
|
@ -70,6 +74,7 @@ jobs:
|
||||||
asset_path: ./manifest.json
|
asset_path: ./manifest.json
|
||||||
asset_name: manifest.json
|
asset_name: manifest.json
|
||||||
asset_content_type: application/json
|
asset_content_type: application/json
|
||||||
|
|
||||||
- name: Upload styles.css
|
- name: Upload styles.css
|
||||||
id: upload-css
|
id: upload-css
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
|
|
|
||||||
5
.prettierignore
Normal file
5
.prettierignore
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
/lib
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
data.json
|
||||||
|
main.js
|
||||||
7
.prettierrc.json
Normal file
7
.prettierrc.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"printWidth": 180,
|
||||||
|
"useTabs": true,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
102
LICENSE.md
102
LICENSE.md
|
|
@ -1,11 +1,12 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
# GNU GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
Preamble
|
## Preamble
|
||||||
|
|
||||||
The GNU General Public License is a free, copyleft license for
|
The GNU General Public License is a free, copyleft license for
|
||||||
software and other kinds of works.
|
software and other kinds of works.
|
||||||
|
|
@ -68,9 +69,9 @@ patents cannot be used to render the program non-free.
|
||||||
The precise terms and conditions for copying, distribution and
|
The precise terms and conditions for copying, distribution and
|
||||||
modification follow.
|
modification follow.
|
||||||
|
|
||||||
TERMS AND CONDITIONS
|
## TERMS AND CONDITIONS
|
||||||
|
|
||||||
0. Definitions.
|
### 0. Definitions.
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU General Public License.
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
|
@ -109,7 +110,7 @@ work under this License, and how to view a copy of this License. If
|
||||||
the interface presents a list of user commands or options, such as a
|
the interface presents a list of user commands or options, such as a
|
||||||
menu, a prominent item in the list meets this criterion.
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
1. Source Code.
|
### 1. Source Code.
|
||||||
|
|
||||||
The "source code" for a work means the preferred form of the work
|
The "source code" for a work means the preferred form of the work
|
||||||
for making modifications to it. "Object code" means any non-source
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
|
@ -151,7 +152,7 @@ Source.
|
||||||
The Corresponding Source for a work in source code form is that
|
The Corresponding Source for a work in source code form is that
|
||||||
same work.
|
same work.
|
||||||
|
|
||||||
2. Basic Permissions.
|
### 2. Basic Permissions.
|
||||||
|
|
||||||
All rights granted under this License are granted for the term of
|
All rights granted under this License are granted for the term of
|
||||||
copyright on the Program, and are irrevocable provided the stated
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
|
@ -176,7 +177,7 @@ your copyrighted material outside their relationship with you.
|
||||||
the conditions stated below. Sublicensing is not allowed; section 10
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
makes it unnecessary.
|
makes it unnecessary.
|
||||||
|
|
||||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
No covered work shall be deemed part of an effective technological
|
No covered work shall be deemed part of an effective technological
|
||||||
measure under any applicable law fulfilling obligations under article
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
|
@ -192,7 +193,7 @@ modification of the work as a means of enforcing, against the work's
|
||||||
users, your or third parties' legal rights to forbid circumvention of
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
technological measures.
|
technological measures.
|
||||||
|
|
||||||
4. Conveying Verbatim Copies.
|
### 4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
You may convey verbatim copies of the Program's source code as you
|
You may convey verbatim copies of the Program's source code as you
|
||||||
receive it, in any medium, provided that you conspicuously and
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
|
@ -205,7 +206,7 @@ recipients a copy of this License along with the Program.
|
||||||
You may charge any price or no price for each copy that you convey,
|
You may charge any price or no price for each copy that you convey,
|
||||||
and you may offer support or warranty protection for a fee.
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
5. Conveying Modified Source Versions.
|
### 5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
You may convey a work based on the Program, or the modifications to
|
You may convey a work based on the Program, or the modifications to
|
||||||
produce it from the Program, in the form of source code under the
|
produce it from the Program, in the form of source code under the
|
||||||
|
|
@ -215,8 +216,8 @@ terms of section 4, provided that you also meet all of these conditions:
|
||||||
it, and giving a relevant date.
|
it, and giving a relevant date.
|
||||||
|
|
||||||
b) The work must carry prominent notices stating that it is
|
b) The work must carry prominent notices stating that it is
|
||||||
released under this License and any conditions added under section
|
released under this License and any conditions added under section 7.
|
||||||
7. This requirement modifies the requirement in section 4 to
|
This requirement modifies the requirement in section 4 to
|
||||||
"keep intact all notices".
|
"keep intact all notices".
|
||||||
|
|
||||||
c) You must license the entire work, as a whole, under this
|
c) You must license the entire work, as a whole, under this
|
||||||
|
|
@ -242,7 +243,7 @@ beyond what the individual works permit. Inclusion of a covered work
|
||||||
in an aggregate does not cause this License to apply to the other
|
in an aggregate does not cause this License to apply to the other
|
||||||
parts of the aggregate.
|
parts of the aggregate.
|
||||||
|
|
||||||
6. Conveying Non-Source Forms.
|
### 6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
You may convey a covered work in object code form under the terms
|
You may convey a covered work in object code form under the terms
|
||||||
of sections 4 and 5, provided that you also convey the
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
|
@ -340,7 +341,7 @@ documented (and with an implementation available to the public in
|
||||||
source code form), and must require no special password or key for
|
source code form), and must require no special password or key for
|
||||||
unpacking, reading or copying.
|
unpacking, reading or copying.
|
||||||
|
|
||||||
7. Additional Terms.
|
### 7. Additional Terms.
|
||||||
|
|
||||||
"Additional permissions" are terms that supplement the terms of this
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
License by making exceptions from one or more of its conditions.
|
License by making exceptions from one or more of its conditions.
|
||||||
|
|
@ -404,7 +405,7 @@ where to find the applicable terms.
|
||||||
form of a separately written license, or stated as exceptions;
|
form of a separately written license, or stated as exceptions;
|
||||||
the above requirements apply either way.
|
the above requirements apply either way.
|
||||||
|
|
||||||
8. Termination.
|
### 8. Termination.
|
||||||
|
|
||||||
You may not propagate or modify a covered work except as expressly
|
You may not propagate or modify a covered work except as expressly
|
||||||
provided under this License. Any attempt otherwise to propagate or
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
|
@ -432,7 +433,7 @@ this License. If your rights have been terminated and not permanently
|
||||||
reinstated, you do not qualify to receive new licenses for the same
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
material under section 10.
|
material under section 10.
|
||||||
|
|
||||||
9. Acceptance Not Required for Having Copies.
|
### 9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
You are not required to accept this License in order to receive or
|
You are not required to accept this License in order to receive or
|
||||||
run a copy of the Program. Ancillary propagation of a covered work
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
|
@ -443,7 +444,7 @@ modify any covered work. These actions infringe copyright if you do
|
||||||
not accept this License. Therefore, by modifying or propagating a
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
covered work, you indicate your acceptance of this License to do so.
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
10. Automatic Licensing of Downstream Recipients.
|
### 10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
Each time you convey a covered work, the recipient automatically
|
Each time you convey a covered work, the recipient automatically
|
||||||
receives a license from the original licensors, to run, modify and
|
receives a license from the original licensors, to run, modify and
|
||||||
|
|
@ -468,7 +469,7 @@ rights granted under this License, and you may not initiate litigation
|
||||||
any patent claim is infringed by making, using, selling, offering for
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
sale, or importing the Program or any portion of it.
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
11. Patents.
|
### 11. Patents.
|
||||||
|
|
||||||
A "contributor" is a copyright holder who authorizes use under this
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
License of the Program or a work on which the Program is based. The
|
License of the Program or a work on which the Program is based. The
|
||||||
|
|
@ -537,7 +538,7 @@ or that patent license was granted, prior to 28 March 2007.
|
||||||
any implied license or other defenses to infringement that may
|
any implied license or other defenses to infringement that may
|
||||||
otherwise be available to you under applicable patent law.
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
12. No Surrender of Others' Freedom.
|
### 12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
If conditions are imposed on you (whether by court order, agreement or
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
|
@ -549,7 +550,7 @@ to collect a royalty for further conveying from those to whom you convey
|
||||||
the Program, the only way you could satisfy both those terms and this
|
the Program, the only way you could satisfy both those terms and this
|
||||||
License would be to refrain entirely from conveying the Program.
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
13. Use with the GNU Affero General Public License.
|
### 13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
Notwithstanding any other provision of this License, you have
|
||||||
permission to link or combine any covered work with a work licensed
|
permission to link or combine any covered work with a work licensed
|
||||||
|
|
@ -560,7 +561,7 @@ but the special requirements of the GNU Affero General Public License,
|
||||||
section 13, concerning interaction through a network will apply to the
|
section 13, concerning interaction through a network will apply to the
|
||||||
combination as such.
|
combination as such.
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
### 14. Revised Versions of this License.
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
the GNU General Public License from time to time. Such new versions will
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
|
@ -586,7 +587,7 @@ permissions. However, no additional obligations are imposed on any
|
||||||
author or copyright holder as a result of your choosing to follow a
|
author or copyright holder as a result of your choosing to follow a
|
||||||
later version.
|
later version.
|
||||||
|
|
||||||
15. Disclaimer of Warranty.
|
### 15. Disclaimer of Warranty.
|
||||||
|
|
||||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
|
@ -597,7 +598,7 @@ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
16. Limitation of Liability.
|
### 16. Limitation of Liability.
|
||||||
|
|
||||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
|
@ -609,7 +610,7 @@ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
SUCH DAMAGES.
|
SUCH DAMAGES.
|
||||||
|
|
||||||
17. Interpretation of Sections 15 and 16.
|
### 17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
If the disclaimer of warranty and limitation of liability provided
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
above cannot be given local legal effect according to their terms,
|
above cannot be given local legal effect according to their terms,
|
||||||
|
|
@ -618,57 +619,4 @@ an absolute waiver of all civil liability in connection with the
|
||||||
Program, unless a warranty or assumption of liability accompanies a
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
copy of the Program in return for a fee.
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
## END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
state the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program does terminal interaction, make it output a short
|
|
||||||
notice like this when it starts in an interactive mode:
|
|
||||||
|
|
||||||
<program> Copyright (C) <year> <name of author>
|
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, your program's commands
|
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
|
||||||
<https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License. But first, please read
|
|
||||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
||||||
|
|
|
||||||
39
README.md
39
README.md
|
|
@ -3,18 +3,23 @@
|
||||||
A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.
|
A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
#### Search by Title
|
#### Search by Title
|
||||||
|
|
||||||
Search a movie, series, anime, game, music release or wiki article by its name across multiple APIs.
|
Search a movie, series, anime, game, music release or wiki article by its name across multiple APIs.
|
||||||
|
|
||||||
#### Search by ID
|
#### Search by ID
|
||||||
|
|
||||||
Allows you to search by an ID that varies from API to API. Concrete information on this feature can be found in the description of the individual APIs.
|
Allows you to search by an ID that varies from API to API. Concrete information on this feature can be found in the description of the individual APIs.
|
||||||
|
|
||||||
#### Templates
|
#### Templates
|
||||||
|
|
||||||
The plugin allows you to set a template note that gets added to the end of any note created by this plugin.
|
The plugin allows you to set a template note that gets added to the end of any note created by this plugin.
|
||||||
The plugin also offers simple "template tgs". E.g. if the template includes `{{ title }}`, it will be replaced by the title of the movie, show or game.
|
The plugin also offers simple "template tgs". E.g. if the template includes `{{ title }}`, it will be replaced by the title of the movie, show or game.
|
||||||
Note that "template tags" are surrounded with two curly braces and that the spaces inside the curly braces are important.
|
Note that "template tags" are surrounded with two curly braces and that the spaces inside the curly braces are important.
|
||||||
|
|
||||||
For arrays there are two special ways of displaying them.
|
For arrays there are two special ways of displaying them.
|
||||||
|
|
||||||
- using `{{ LIST:variable_name }}` will result in
|
- using `{{ LIST:variable_name }}` will result in
|
||||||
```
|
```
|
||||||
- element 1
|
- element 1
|
||||||
|
|
@ -32,40 +37,48 @@ Available variables that can be used in template tags are the same variables fro
|
||||||
I also published my own templates [here](https://github.com/mProjectsCode/obsidian-media-db-templates).
|
I also published my own templates [here](https://github.com/mProjectsCode/obsidian-media-db-templates).
|
||||||
|
|
||||||
#### Metadata field customization
|
#### Metadata field customization
|
||||||
|
|
||||||
Allows you to rename the metadata fields this plugin generates through mappings.
|
Allows you to rename the metadata fields this plugin generates through mappings.
|
||||||
|
|
||||||
A mapping has to follow this syntax `[origional property name] -> [new property name]`.
|
A mapping has to follow this syntax `[origional property name] -> [new property name]`.
|
||||||
Multiple mappings are separated by a new line.
|
Multiple mappings are separated by a new line.
|
||||||
So e.g.:
|
So e.g.:
|
||||||
|
|
||||||
```
|
```
|
||||||
title -> name
|
title -> name
|
||||||
year -> releaseYear
|
year -> releaseYear
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Bulk Import
|
#### Bulk Import
|
||||||
|
|
||||||
The plugin allows you to import your preexisting media collection and upgrade them to Media DB entries.
|
The plugin allows you to import your preexisting media collection and upgrade them to Media DB entries.
|
||||||
|
|
||||||
##### Prerequisites
|
##### Prerequisites
|
||||||
|
|
||||||
The preexisting media notes must be inside a folder in your vault.
|
The preexisting media notes must be inside a folder in your vault.
|
||||||
For the plugin to be able to query them they need one metadata field that is used as the title the piece of media is searched by.
|
For the plugin to be able to query them they need one metadata field that is used as the title the piece of media is searched by.
|
||||||
This can be achieved by for example using a `csv` import plugin to import an existing list from outside of obsidian.
|
This can be achieved by for example using a `csv` import plugin to import an existing list from outside of obsidian.
|
||||||
|
|
||||||
##### Importing
|
##### Importing
|
||||||
|
|
||||||
To start the import process, right-click on the folder and select the `Import folder as Media DB entries` option.
|
To start the import process, right-click on the folder and select the `Import folder as Media DB entries` option.
|
||||||
Then specify the API to search, if the current note content and metadata should be appended to the Media DB entry and the name of the metadata field that contains the title of the piece of media.
|
Then specify the API to search, if the current note content and metadata should be appended to the Media DB entry and the name of the metadata field that contains the title of the piece of media.
|
||||||
|
|
||||||
Then the plugin will go through every file in the folder and prompt you to select from the search results.
|
Then the plugin will go through every file in the folder and prompt you to select from the search results.
|
||||||
|
|
||||||
##### Post import
|
##### Post import
|
||||||
|
|
||||||
After all files have been imported or the import was canceled, you will find the new entries as well as an error report that contains any errors or skipped/canceled files in the folder specified in the setting of the plugin.
|
After all files have been imported or the import was canceled, you will find the new entries as well as an error report that contains any errors or skipped/canceled files in the folder specified in the setting of the plugin.
|
||||||
|
|
||||||
### How to install
|
### How to install
|
||||||
|
|
||||||
**The plugin is now released, so it can be installed directly through obsidian's plugin installer.**
|
**The plugin is now released, so it can be installed directly through obsidian's plugin installer.**
|
||||||
|
|
||||||
Alternatively, you can manually download the zip archive from the latest release here on GitHub.
|
Alternatively, you can manually download the zip archive from the latest release here on GitHub.
|
||||||
After downloading, extract the archive into the `.obsidian/plugins` folder in your vault.
|
After downloading, extract the archive into the `.obsidian/plugins` folder in your vault.
|
||||||
|
|
||||||
The folder structure should look like this:
|
The folder structure should look like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
[path to your vault]
|
[path to your vault]
|
||||||
|_ .obsidian
|
|_ .obsidian
|
||||||
|
|
@ -76,8 +89,8 @@ The folder structure should look like this:
|
||||||
|_ styles.css
|
|_ styles.css
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### How to use
|
### How to use
|
||||||
|
|
||||||
(pictures are coming)
|
(pictures are coming)
|
||||||
|
|
||||||
Once you have installed this plugin, you will find a database icon in the left ribbon.
|
Once you have installed this plugin, you will find a database icon in the left ribbon.
|
||||||
|
|
@ -88,6 +101,7 @@ After clicking search, a new popup will open prompting you to select from the se
|
||||||
Now you select the result you want and the plugin will cast it's magic and create a new note in your vault, that contains the metadata of the selected search result.
|
Now you select the result you want and the plugin will cast it's magic and create a new note in your vault, that contains the metadata of the selected search result.
|
||||||
|
|
||||||
### Currently supported media types
|
### Currently supported media types
|
||||||
|
|
||||||
- movies (including specials)
|
- movies (including specials)
|
||||||
- series (including OVAs)
|
- series (including OVAs)
|
||||||
- games
|
- games
|
||||||
|
|
@ -95,8 +109,9 @@ Now you select the result you want and the plugin will cast it's magic and creat
|
||||||
- wiki articles
|
- wiki articles
|
||||||
|
|
||||||
### Currently supported APIs:
|
### Currently supported APIs:
|
||||||
|
|
||||||
| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
|
| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
|
||||||
|------------------------------------------------------|---------------------------------------------------------------------------------------------------|--------------------------------|------------------------------------------------------------------------------|--------------------------------|--------------------|
|
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------- | ------------------------------ | ------------------ |
|
||||||
| [Jikan](https://jikan.moe/) | Jikan is an API that uses [My Anime List](https://myanimelist.net) and offers metadata for anime. | series, movies, specials, OVAs | No | 60 per minute and 3 per second | Yes |
|
| [Jikan](https://jikan.moe/) | Jikan is an API that uses [My Anime List](https://myanimelist.net) and offers metadata for anime. | series, movies, specials, OVAs | No | 60 per minute and 3 per second | Yes |
|
||||||
| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movie, series and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No |
|
| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movie, series and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No |
|
||||||
| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
|
| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
|
||||||
|
|
@ -104,12 +119,14 @@ Now you select the result you want and the plugin will cast it's magic and creat
|
||||||
| [Steam](https://store.steampowered.com/) | The Steam API offers information on all steam games. | games | No | 10000 per day | No |
|
| [Steam](https://store.steampowered.com/) | The Steam API offers information on all steam games. | games | No | 10000 per day | No |
|
||||||
|
|
||||||
#### Notes
|
#### Notes
|
||||||
|
|
||||||
- [Jikan](https://jikan.moe/)
|
- [Jikan](https://jikan.moe/)
|
||||||
- sometimes the api is very slow, this is normal
|
- sometimes the api is very slow, this is normal
|
||||||
- you need to use the title the anime has on [My Anime List](https://myanimelist.net), which is in most cases the japanese title
|
- you need to use the title the anime has on [My Anime List](https://myanimelist.net), which is in most cases the japanese title
|
||||||
- e.g. instead of "Demon Slayer" you have to search "Kimetsu no Yaiba"
|
- e.g. instead of "Demon Slayer" you have to search "Kimetsu no Yaiba"
|
||||||
|
|
||||||
#### Search by ID
|
#### Search by ID
|
||||||
|
|
||||||
- [Jikan](https://jikan.moe/)
|
- [Jikan](https://jikan.moe/)
|
||||||
- the ID you need is the ID of the anime on [My Anime List](https://myanimelist.net)
|
- the ID you need is the ID of the anime on [My Anime List](https://myanimelist.net)
|
||||||
- you can find this ID in the URL
|
- you can find this ID in the URL
|
||||||
|
|
@ -127,36 +144,54 @@ Now you select the result you want and the plugin will cast it's magic and creat
|
||||||
- e.g. for "Factorio" the URL looks like this `https://store.steampowered.com/app/427520/Factorio/` so the ID is `427520`
|
- e.g. for "Factorio" the URL looks like this `https://store.steampowered.com/app/427520/Factorio/` so the ID is `427520`
|
||||||
|
|
||||||
### Problems, unexpected behavior or improvement suggestions?
|
### Problems, unexpected behavior or improvement suggestions?
|
||||||
|
|
||||||
You are more than welcome to open an issue on [GitHub](https://github.com/mProjectsCode/obsidian-media-db-plugin/issues).
|
You are more than welcome to open an issue on [GitHub](https://github.com/mProjectsCode/obsidian-media-db-plugin/issues).
|
||||||
|
|
||||||
### Changelog
|
### Changelog
|
||||||
|
|
||||||
|
#### 0.5.0
|
||||||
|
|
||||||
|
- New simple search modal, select the media type and search all applicable APIs
|
||||||
|
- More data for Board Games
|
||||||
|
- Actors and Streaming Platforms for Movies and Series
|
||||||
|
- Separate new file location for all media types
|
||||||
|
- Separate command for each media type
|
||||||
|
- Fix problems with closing of preview modal
|
||||||
|
|
||||||
#### 0.3.2
|
#### 0.3.2
|
||||||
|
|
||||||
- Added Board Game Geek API (documentation pending)
|
- Added Board Game Geek API (documentation pending)
|
||||||
- More information in the search results
|
- More information in the search results
|
||||||
- various fixes
|
- various fixes
|
||||||
|
|
||||||
#### 0.3.1
|
#### 0.3.1
|
||||||
|
|
||||||
- various fixes
|
- various fixes
|
||||||
|
|
||||||
#### 0.3.0
|
#### 0.3.0
|
||||||
|
|
||||||
- Added bulk import. Import a folder of media notes as Media DB entries (thanks to [PaperOrb](https://github.com/PaperOrb) on GitHub for their input and for helping me test this feature)
|
- Added bulk import. Import a folder of media notes as Media DB entries (thanks to [PaperOrb](https://github.com/PaperOrb) on GitHub for their input and for helping me test this feature)
|
||||||
- Added a custom result select modal that allows you to select multiple results at once
|
- Added a custom result select modal that allows you to select multiple results at once
|
||||||
- Fixed a bug where the note creation would fail when the metadata included a field with the values `null` or `undefined`
|
- Fixed a bug where the note creation would fail when the metadata included a field with the values `null` or `undefined`
|
||||||
|
|
||||||
#### 0.2.1
|
#### 0.2.1
|
||||||
|
|
||||||
- fixed a small bug with the initial selection of an API in the ID search modal
|
- fixed a small bug with the initial selection of an API in the ID search modal
|
||||||
|
|
||||||
#### 0.2.0
|
#### 0.2.0
|
||||||
|
|
||||||
- Added the option to rename metadata fields through property mappings
|
- Added the option to rename metadata fields through property mappings
|
||||||
- fixed note creation falling, when the folder set in the settings did not exist
|
- fixed note creation falling, when the folder set in the settings did not exist
|
||||||
|
|
||||||
### Contributions
|
### Contributions
|
||||||
|
|
||||||
Thank you for wanting to contribute to this project.
|
Thank you for wanting to contribute to this project.
|
||||||
|
|
||||||
Contributions are always welcome. If you have an idea, feel free to open a feature request under the issue tab or even create a pull request.
|
Contributions are always welcome. If you have an idea, feel free to open a feature request under the issue tab or even create a pull request.
|
||||||
|
|
||||||
### Credits
|
### Credits
|
||||||
|
|
||||||
Credits go to:
|
Credits go to:
|
||||||
|
|
||||||
- https://github.com/anpigon/obsidian-book-search-plugin for some inspiration and the idea to make this plugin
|
- https://github.com/anpigon/obsidian-book-search-plugin for some inspiration and the idea to make this plugin
|
||||||
- https://github.com/liamcain/obsidian-periodic-notes for 99% of `Suggest.ts` and `FolderSuggest.ts`
|
- https://github.com/liamcain/obsidian-periodic-notes for 99% of `Suggest.ts` and `FolderSuggest.ts`
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { RequestUrlParam, RequestUrlResponse } from "obsidian";
|
import { RequestUrlParam, RequestUrlResponse } from 'obsidian';
|
||||||
|
|
||||||
export function requestUrl(request: RequestUrlParam): Promise<RequestUrlResponse> {
|
export function requestUrl(request: RequestUrlParam): Promise<RequestUrlResponse> {
|
||||||
return fetch(request.url, {
|
return fetch(request.url, {
|
||||||
method: request.method,
|
method: request.method,
|
||||||
headers: request.headers,
|
headers: request.headers,
|
||||||
body: request.body,
|
body: request.body,
|
||||||
}).then(async (response) => {
|
}).then(async response => {
|
||||||
if (response.status >= 400 && request.throw) {
|
if (response.status >= 400 && request.throw) {
|
||||||
throw new Error(`Request failed, ${response.status}`);
|
throw new Error(`Request failed, ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,16 @@ import builtins from 'builtin-modules';
|
||||||
import esbuildSvelte from 'esbuild-svelte';
|
import esbuildSvelte from 'esbuild-svelte';
|
||||||
import sveltePreprocess from 'svelte-preprocess';
|
import sveltePreprocess from 'svelte-preprocess';
|
||||||
|
|
||||||
const banner =
|
const banner = `/*
|
||||||
`/*
|
|
||||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||||
if you want to view the source, please visit the github repository of this plugin
|
if you want to view the source, please visit the github repository of this plugin
|
||||||
*/
|
*/
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const prod = (process.argv[2] === 'production');
|
const prod = process.argv[2] === 'production';
|
||||||
|
|
||||||
esbuild.build({
|
esbuild
|
||||||
|
.build({
|
||||||
banner: {
|
banner: {
|
||||||
js: banner,
|
js: banner,
|
||||||
},
|
},
|
||||||
|
|
@ -43,7 +43,8 @@ esbuild.build({
|
||||||
'@codemirror/text',
|
'@codemirror/text',
|
||||||
'@codemirror/tooltip',
|
'@codemirror/tooltip',
|
||||||
'@codemirror/view',
|
'@codemirror/view',
|
||||||
...builtins],
|
...builtins,
|
||||||
|
],
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
watch: !prod,
|
watch: !prod,
|
||||||
target: 'es2016',
|
target: 'es2016',
|
||||||
|
|
@ -57,4 +58,5 @@ esbuild.build({
|
||||||
preprocess: sveltePreprocess(),
|
preprocess: sveltePreprocess(),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}).catch(() => process.exit(1));
|
})
|
||||||
|
.catch(() => process.exit(1));
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"roots": [
|
roots: ['<rootDir>/src', '<rootDir>'],
|
||||||
"<rootDir>/src",
|
testMatch: ['**/__tests__/**/*.+(ts|tsx|js)', '**/?(*.)+(spec|test).+(ts|tsx|js)'],
|
||||||
"<rootDir>"
|
transform: {
|
||||||
],
|
'^.+\\.(ts|tsx)$': 'ts-jest',
|
||||||
"testMatch": [
|
|
||||||
"**/__tests__/**/*.+(ts|tsx|js)",
|
|
||||||
"**/?(*.)+(spec|test).+(ts|tsx|js)"
|
|
||||||
],
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(ts|tsx)$": "ts-jest"
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "obsidian-media-db-plugin",
|
"id": "obsidian-media-db-plugin",
|
||||||
"name": "Media DB Plugin",
|
"name": "Media DB Plugin",
|
||||||
"version": "0.4.1",
|
"version": "0.5.0",
|
||||||
"minAppVersion": "0.14.0",
|
"minAppVersion": "0.14.0",
|
||||||
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
||||||
"author": "Moritz Jung",
|
"author": "Moritz Jung",
|
||||||
|
|
|
||||||
42
package-lock.json
generated
42
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-media-db-plugin",
|
"name": "obsidian-media-db-plugin",
|
||||||
"version": "0.3.2",
|
"version": "0.4.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-media-db-plugin",
|
"name": "obsidian-media-db-plugin",
|
||||||
"version": "0.3.2",
|
"version": "0.4.1",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ts-node": "^10.8.1",
|
"ts-node": "^10.8.1",
|
||||||
|
|
@ -22,9 +22,11 @@
|
||||||
"builtin-modules": "^3.2.0",
|
"builtin-modules": "^3.2.0",
|
||||||
"esbuild": "0.13.12",
|
"esbuild": "0.13.12",
|
||||||
"esbuild-svelte": "^0.7.1",
|
"esbuild-svelte": "^0.7.1",
|
||||||
|
"eslint-plugin-only-warn": "^1.0.3",
|
||||||
"jest": "^28.1.2",
|
"jest": "^28.1.2",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"obsidian": "latest",
|
"obsidian": "latest",
|
||||||
|
"prettier": "2.7.1",
|
||||||
"svelte": "^3.50.1",
|
"svelte": "^3.50.1",
|
||||||
"svelte-preprocess": "^4.10.7",
|
"svelte-preprocess": "^4.10.7",
|
||||||
"ts-jest": "^28.0.5",
|
"ts-jest": "^28.0.5",
|
||||||
|
|
@ -2493,6 +2495,15 @@
|
||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-only-warn": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-only-warn/-/eslint-plugin-only-warn-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-XQOX/TfLoLw6h8ky51d29uUjXRTQHqBGXPylDEmy5fe/w7LIOnp8MA24b1OSMEn9BQoKow1q3g1kLe5/9uBTvw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-scope": {
|
"node_modules/eslint-scope": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
|
@ -4353,6 +4364,21 @@
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pretty-format": {
|
"node_modules/pretty-format": {
|
||||||
"version": "28.1.1",
|
"version": "28.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.1.tgz",
|
||||||
|
|
@ -7229,6 +7255,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eslint-plugin-only-warn": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-only-warn/-/eslint-plugin-only-warn-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-XQOX/TfLoLw6h8ky51d29uUjXRTQHqBGXPylDEmy5fe/w7LIOnp8MA24b1OSMEn9BQoKow1q3g1kLe5/9uBTvw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"eslint-scope": {
|
"eslint-scope": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
|
@ -8623,6 +8655,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"pretty-format": {
|
"pretty-format": {
|
||||||
"version": "28.1.1",
|
"version": "28.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-media-db-plugin",
|
"name": "obsidian-media-db-plugin",
|
||||||
"version": "0.4.1",
|
"version": "0.5.0",
|
||||||
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||||
"version": "node version-bump.mjs && git add manifest.json versions.json",
|
"version": "node version-bump.mjs && git add manifest.json versions.json",
|
||||||
"test": "jest"
|
"test": "jest",
|
||||||
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Moritz Jung",
|
"author": "Moritz Jung",
|
||||||
|
|
@ -22,6 +23,7 @@
|
||||||
"builtin-modules": "^3.2.0",
|
"builtin-modules": "^3.2.0",
|
||||||
"esbuild": "0.13.12",
|
"esbuild": "0.13.12",
|
||||||
"esbuild-svelte": "^0.7.1",
|
"esbuild-svelte": "^0.7.1",
|
||||||
|
"eslint-plugin-only-warn": "^1.0.3",
|
||||||
"jest": "^28.1.2",
|
"jest": "^28.1.2",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"obsidian": "latest",
|
"obsidian": "latest",
|
||||||
|
|
@ -29,7 +31,8 @@
|
||||||
"svelte-preprocess": "^4.10.7",
|
"svelte-preprocess": "^4.10.7",
|
||||||
"ts-jest": "^28.0.5",
|
"ts-jest": "^28.0.5",
|
||||||
"tslib": "2.3.1",
|
"tslib": "2.3.1",
|
||||||
"typescript": "4.4.4"
|
"typescript": "4.4.4",
|
||||||
|
"prettier": "2.7.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ts-node": "^10.8.1",
|
"ts-node": "^10.8.1",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
export abstract class APIModel {
|
export abstract class APIModel {
|
||||||
apiName: string;
|
apiName: string;
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
apiDescription: string;
|
apiDescription: string;
|
||||||
types: string[];
|
types: MediaType[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function should query the api and return a list of matches. The matches should be caped at 20.
|
* This function should query the api and return a list of matches. The matches should be caped at 20.
|
||||||
|
|
@ -15,12 +16,11 @@ export abstract class APIModel {
|
||||||
|
|
||||||
abstract getById(id: string): Promise<MediaTypeModel>;
|
abstract getById(id: string): Promise<MediaTypeModel>;
|
||||||
|
|
||||||
hasType(type: string): boolean {
|
hasType(type: MediaType): boolean {
|
||||||
return this.types.contains(type);
|
return this.types.contains(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for future use (https://github.com/mProjectsCode/obsidian-media-db-plugin/issues/5)
|
hasTypeOverlap(types: MediaType[]): boolean {
|
||||||
hasTypeOverlap(types: string[]): boolean {
|
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
if (this.hasType(type)) {
|
if (this.hasType(type)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import {APIModel} from '../APIModel';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../../main';
|
import MediaDbPlugin from '../../main';
|
||||||
import { BoardGameModel } from 'src/models/BoardGameModel';
|
import { BoardGameModel } from 'src/models/BoardGameModel';
|
||||||
import {debugLog} from '../../utils/Utils';
|
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
|
||||||
export class BoardGameGeekAPI extends APIModel {
|
export class BoardGameGeekAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -15,7 +15,7 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
this.apiName = 'BoardGameGeekAPI';
|
this.apiName = 'BoardGameGeekAPI';
|
||||||
this.apiDescription = 'A free API for BoardGameGeek things.';
|
this.apiDescription = 'A free API for BoardGameGeek things.';
|
||||||
this.apiUrl = 'https://api.geekdo.com/xmlapi';
|
this.apiUrl = 'https://api.geekdo.com/xmlapi';
|
||||||
this.types = ['boardgames'];
|
this.types = [MediaType.BoardGame];
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||||
|
|
@ -33,22 +33,24 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
const data = fetchData.text;
|
const data = fetchData.text;
|
||||||
const response = new window.DOMParser().parseFromString(data, 'text/xml');
|
const response = new window.DOMParser().parseFromString(data, 'text/xml');
|
||||||
|
|
||||||
debugLog(response);
|
console.debug(response);
|
||||||
|
|
||||||
let ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const boardgame of Array.from(response.querySelectorAll('boardgame'))) {
|
for (const boardgame of Array.from(response.querySelectorAll('boardgame'))) {
|
||||||
const id = boardgame.attributes.getNamedItem('objectid')!.value;
|
const id = boardgame.attributes.getNamedItem('objectid')!.value;
|
||||||
const title = boardgame.querySelector('name')!.textContent!;
|
const title = boardgame.querySelector('name[primary=true]')?.textContent ?? boardgame.querySelector('name')!.textContent!;
|
||||||
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
||||||
|
|
||||||
ret.push(new BoardGameModel({
|
ret.push(
|
||||||
|
new BoardGameModel({
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
englishTitle: title,
|
englishTitle: title,
|
||||||
year,
|
year,
|
||||||
} as BoardGameModel));
|
} as BoardGameModel)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -68,17 +70,22 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
|
|
||||||
const data = fetchData.text;
|
const data = fetchData.text;
|
||||||
const response = new window.DOMParser().parseFromString(data, 'text/xml');
|
const response = new window.DOMParser().parseFromString(data, 'text/xml');
|
||||||
debugLog(response);
|
console.debug(response);
|
||||||
|
|
||||||
const boardgame = response.querySelector('boardgame')!;
|
const boardgame = response.querySelector('boardgame')!;
|
||||||
const title = boardgame.querySelector('name')!.textContent!;
|
const title = boardgame.querySelector('name[primary=true]')!.textContent!;
|
||||||
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
||||||
const image = boardgame.querySelector('image')?.textContent ?? undefined;
|
const image = boardgame.querySelector('image')?.textContent ?? undefined;
|
||||||
const onlineRating = Number.parseFloat(boardgame.querySelector('statistics ratings average')?.textContent ?? '');
|
const onlineRating = Number.parseFloat(boardgame.querySelector('statistics ratings average')?.textContent ?? '0');
|
||||||
const genres = Array.from(boardgame.querySelectorAll('boardgamecategory')).map(n => n!.textContent!);
|
const genres = Array.from(boardgame.querySelectorAll('boardgamecategory')).map(n => n!.textContent!);
|
||||||
|
const complexityRating = Number.parseFloat(boardgame.querySelector('averageweight')?.textContent ?? '0');
|
||||||
|
const minPlayers = Number.parseFloat(boardgame.querySelector('minplayers')?.textContent ?? '0');
|
||||||
|
const maxPlayers = Number.parseFloat(boardgame.querySelector('maxplayers')?.textContent ?? '0');
|
||||||
|
const playtime = (boardgame.querySelector('playingtime')?.textContent ?? 'unknown') + ' minutes';
|
||||||
|
const publishers = Array.from(boardgame.querySelectorAll('boardgamepublisher')).map(n => n!.textContent!);
|
||||||
|
|
||||||
const model = new BoardGameModel({
|
const model = new BoardGameModel({
|
||||||
title,
|
title: title,
|
||||||
englishTitle: title,
|
englishTitle: title,
|
||||||
year: year === '0' ? '' : year,
|
year: year === '0' ? '' : year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
|
|
@ -87,7 +94,13 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
|
|
||||||
genres: genres,
|
genres: genres,
|
||||||
onlineRating: onlineRating,
|
onlineRating: onlineRating,
|
||||||
|
complexityRating: complexityRating,
|
||||||
|
minPlayers: minPlayers,
|
||||||
|
maxPlayers: maxPlayers,
|
||||||
|
playtime: playtime,
|
||||||
|
publishers: publishers,
|
||||||
image: image,
|
image: image,
|
||||||
|
|
||||||
released: true,
|
released: true,
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
|
|
@ -97,6 +110,5 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
} as BoardGameModel);
|
} as BoardGameModel);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { APIModel } from '../APIModel';
|
import { APIModel } from '../APIModel';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../../main';
|
import MediaDbPlugin from '../../main';
|
||||||
import {debugLog} from '../../utils/Utils';
|
|
||||||
|
|
||||||
// WIP
|
// WIP
|
||||||
export class LocGovAPI extends APIModel {
|
export class LocGovAPI extends APIModel {
|
||||||
|
|
@ -25,14 +24,14 @@ export class LocGovAPI extends APIModel {
|
||||||
|
|
||||||
const searchUrl = `https://www.loc.gov/search/?q=${encodeURIComponent(title)}&fo=json&c=20`;
|
const searchUrl = `https://www.loc.gov/search/?q=${encodeURIComponent(title)}&fo=json&c=20`;
|
||||||
const fetchData = await fetch(searchUrl);
|
const fetchData = await fetch(searchUrl);
|
||||||
debugLog(fetchData);
|
console.debug(fetchData);
|
||||||
|
|
||||||
if (fetchData.status !== 200) {
|
if (fetchData.status !== 200) {
|
||||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetchData.json();
|
const data = await fetchData.json();
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
let ret: MediaTypeModel[] = [];
|
let ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
throw new Error('MDB | Under construction, API implementation not finished');
|
throw new Error('MDB | Under construction, API implementation not finished');
|
||||||
|
|
@ -50,7 +49,7 @@ export class LocGovAPI extends APIModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetchData.json();
|
const data = await fetchData.json();
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
const result = data.data;
|
const result = data.data;
|
||||||
|
|
||||||
const type = this.typeMappings.get(result.type.toLowerCase());
|
const type = this.typeMappings.get(result.type.toLowerCase());
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import {MediaTypeModel} from '../../models/MediaTypeModel';
|
||||||
import { MovieModel } from '../../models/MovieModel';
|
import { MovieModel } from '../../models/MovieModel';
|
||||||
import MediaDbPlugin from '../../main';
|
import MediaDbPlugin from '../../main';
|
||||||
import { SeriesModel } from '../../models/SeriesModel';
|
import { SeriesModel } from '../../models/SeriesModel';
|
||||||
import {debugLog} from '../../utils/Utils';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
|
||||||
export class MALAPI extends APIModel {
|
export class MALAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -16,7 +16,7 @@ export class MALAPI extends APIModel {
|
||||||
this.apiName = 'MALAPI';
|
this.apiName = 'MALAPI';
|
||||||
this.apiDescription = 'A free API for Anime. Some results may take a long time to load.';
|
this.apiDescription = 'A free API for Anime. Some results may take a long time to load.';
|
||||||
this.apiUrl = 'https://jikan.moe/';
|
this.apiUrl = 'https://jikan.moe/';
|
||||||
this.types = ['movie', 'series', 'anime'];
|
this.types = [MediaType.Movie, MediaType.Series];
|
||||||
this.typeMappings = new Map<string, string>();
|
this.typeMappings = new Map<string, string>();
|
||||||
this.typeMappings.set('movie', 'movie');
|
this.typeMappings.set('movie', 'movie');
|
||||||
this.typeMappings.set('special', 'special');
|
this.typeMappings.set('special', 'special');
|
||||||
|
|
@ -30,46 +30,52 @@ export class MALAPI extends APIModel {
|
||||||
const searchUrl = `https://api.jikan.moe/v4/anime?q=${encodeURIComponent(title)}&limit=20${this.plugin.settings.sfwFilter ? '&sfw' : ''}`;
|
const searchUrl = `https://api.jikan.moe/v4/anime?q=${encodeURIComponent(title)}&limit=20${this.plugin.settings.sfwFilter ? '&sfw' : ''}`;
|
||||||
|
|
||||||
const fetchData = await fetch(searchUrl);
|
const fetchData = await fetch(searchUrl);
|
||||||
debugLog(fetchData);
|
console.debug(fetchData);
|
||||||
if (fetchData.status !== 200) {
|
if (fetchData.status !== 200) {
|
||||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||||
}
|
}
|
||||||
const data = await fetchData.json();
|
const data = await fetchData.json();
|
||||||
|
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
|
|
||||||
let ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const result of data.data) {
|
for (const result of data.data) {
|
||||||
const type = this.typeMappings.get(result.type?.toLowerCase());
|
const type = this.typeMappings.get(result.type?.toLowerCase());
|
||||||
if (type === undefined) {
|
if (type === undefined) {
|
||||||
ret.push(new MovieModel({
|
ret.push(
|
||||||
|
new MovieModel({
|
||||||
subType: '',
|
subType: '',
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title_english ?? result.title,
|
englishTitle: result.title_english ?? result.title,
|
||||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.mal_id,
|
id: result.mal_id,
|
||||||
} as MovieModel));
|
} as MovieModel)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (type === 'movie' || type === 'special') {
|
if (type === 'movie' || type === 'special') {
|
||||||
ret.push(new MovieModel({
|
ret.push(
|
||||||
|
new MovieModel({
|
||||||
subType: type,
|
subType: type,
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title_english ?? result.title,
|
englishTitle: result.title_english ?? result.title,
|
||||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.mal_id,
|
id: result.mal_id,
|
||||||
} as MovieModel));
|
} as MovieModel)
|
||||||
|
);
|
||||||
} else if (type === 'series' || type === 'ova') {
|
} else if (type === 'series' || type === 'ova') {
|
||||||
ret.push(new SeriesModel({
|
ret.push(
|
||||||
|
new SeriesModel({
|
||||||
subType: type,
|
subType: type,
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title_english ?? result.title,
|
englishTitle: result.title_english ?? result.title,
|
||||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.mal_id,
|
id: result.mal_id,
|
||||||
} as SeriesModel));
|
} as SeriesModel)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +85,7 @@ export class MALAPI extends APIModel {
|
||||||
async getById(id: string): Promise<MediaTypeModel> {
|
async getById(id: string): Promise<MediaTypeModel> {
|
||||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||||
|
|
||||||
const searchUrl = `https://api.jikan.moe/v4/anime/${encodeURIComponent(id)}`;
|
const searchUrl = `https://api.jikan.moe/v4/anime/${encodeURIComponent(id)}/full`;
|
||||||
const fetchData = await fetch(searchUrl);
|
const fetchData = await fetch(searchUrl);
|
||||||
|
|
||||||
if (fetchData.status !== 200) {
|
if (fetchData.status !== 200) {
|
||||||
|
|
@ -87,7 +93,7 @@ export class MALAPI extends APIModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetchData.json();
|
const data = await fetchData.json();
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
const result = data.data;
|
const result = data.data;
|
||||||
|
|
||||||
const type = this.typeMappings.get(result.type?.toLowerCase());
|
const type = this.typeMappings.get(result.type?.toLowerCase());
|
||||||
|
|
@ -105,10 +111,12 @@ export class MALAPI extends APIModel {
|
||||||
producer: result.studios?.map((x: any) => x.name).join(', ') ?? 'unknown',
|
producer: result.studios?.map((x: any) => x.name).join(', ') ?? 'unknown',
|
||||||
duration: result.duration ?? 'unknown',
|
duration: result.duration ?? 'unknown',
|
||||||
onlineRating: result.score ?? 0,
|
onlineRating: result.score ?? 0,
|
||||||
|
actors: [],
|
||||||
image: result.images?.jpg?.image_url ?? '',
|
image: result.images?.jpg?.image_url ?? '',
|
||||||
|
|
||||||
released: true,
|
released: true,
|
||||||
premiere: (new Date(result.aired?.from)).toLocaleDateString() ?? 'unknown',
|
premiere: new Date(result.aired?.from).toLocaleDateString() ?? 'unknown',
|
||||||
|
streamingServices: result.streaming?.map((x: any) => x.name) ?? [],
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
watched: false,
|
watched: false,
|
||||||
|
|
@ -134,10 +142,12 @@ export class MALAPI extends APIModel {
|
||||||
producer: result.studios?.map((x: any) => x.name).join(', ') ?? 'unknown',
|
producer: result.studios?.map((x: any) => x.name).join(', ') ?? 'unknown',
|
||||||
duration: result.duration ?? 'unknown',
|
duration: result.duration ?? 'unknown',
|
||||||
onlineRating: result.score ?? 0,
|
onlineRating: result.score ?? 0,
|
||||||
|
actors: [],
|
||||||
image: result.images?.jpg?.image_url ?? '',
|
image: result.images?.jpg?.image_url ?? '',
|
||||||
|
|
||||||
released: true,
|
released: true,
|
||||||
premiere: (new Date(result.aired?.from)).toLocaleDateString() ?? 'unknown',
|
premiere: new Date(result.aired?.from).toLocaleDateString() ?? 'unknown',
|
||||||
|
streamingServices: result.streaming?.map((x: any) => x.name) ?? [],
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
watched: false,
|
watched: false,
|
||||||
|
|
@ -162,11 +172,12 @@ export class MALAPI extends APIModel {
|
||||||
episodes: result.episodes,
|
episodes: result.episodes,
|
||||||
duration: result.duration ?? 'unknown',
|
duration: result.duration ?? 'unknown',
|
||||||
onlineRating: result.score ?? 0,
|
onlineRating: result.score ?? 0,
|
||||||
|
streamingServices: result.streaming?.map((x: any) => x.name) ?? [],
|
||||||
image: result.images?.jpg?.image_url ?? '',
|
image: result.images?.jpg?.image_url ?? '',
|
||||||
|
|
||||||
released: true,
|
released: true,
|
||||||
airedFrom: (new Date(result.aired?.from)).toLocaleDateString() ?? 'unknown',
|
airedFrom: new Date(result.aired?.from).toLocaleDateString() ?? 'unknown',
|
||||||
airedTo: (new Date(result.aired?.to)).toLocaleDateString() ?? 'unknown',
|
airedTo: new Date(result.aired?.to).toLocaleDateString() ?? 'unknown',
|
||||||
airing: result.airing,
|
airing: result.airing,
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ import {MediaTypeModel} from '../../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../../main';
|
import MediaDbPlugin from '../../main';
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
import { MusicReleaseModel } from '../../models/MusicReleaseModel';
|
import { MusicReleaseModel } from '../../models/MusicReleaseModel';
|
||||||
import {contactEmail, debugLog, mediaDbVersion, pluginName} from '../../utils/Utils';
|
import { contactEmail, mediaDbVersion, pluginName } from '../../utils/Utils';
|
||||||
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
|
||||||
export class MusicBrainzAPI extends APIModel {
|
export class MusicBrainzAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -15,7 +16,7 @@ export class MusicBrainzAPI extends APIModel {
|
||||||
this.apiName = 'MusicBrainz API';
|
this.apiName = 'MusicBrainz API';
|
||||||
this.apiDescription = 'Free API for music albums.';
|
this.apiDescription = 'Free API for music albums.';
|
||||||
this.apiUrl = 'https://musicbrainz.org/';
|
this.apiUrl = 'https://musicbrainz.org/';
|
||||||
this.types = ['music'];
|
this.types = [MediaType.MusicRelease];
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||||
|
|
@ -30,29 +31,31 @@ export class MusicBrainzAPI extends APIModel {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
debugLog(fetchData);
|
console.debug(fetchData);
|
||||||
|
|
||||||
if (fetchData.status !== 200) {
|
if (fetchData.status !== 200) {
|
||||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetchData.json;
|
const data = await fetchData.json;
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
let ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const result of data['release-groups']) {
|
for (const result of data['release-groups']) {
|
||||||
ret.push(new MusicReleaseModel({
|
ret.push(
|
||||||
|
new MusicReleaseModel({
|
||||||
type: 'musicRelease',
|
type: 'musicRelease',
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title,
|
englishTitle: result.title,
|
||||||
year: (new Date(result['first-release-date'])).getFullYear().toString(),
|
year: new Date(result['first-release-date']).getFullYear().toString(),
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
url: '',
|
url: '',
|
||||||
id: result.id,
|
id: result.id,
|
||||||
|
|
||||||
artists: result['artist-credit'].map((a: any) => a.name),
|
artists: result['artist-credit'].map((a: any) => a.name),
|
||||||
subType: result['primary-type'],
|
subType: result['primary-type'],
|
||||||
} as MusicReleaseModel));
|
} as MusicReleaseModel)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -74,14 +77,14 @@ export class MusicBrainzAPI extends APIModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetchData.json;
|
const data = await fetchData.json;
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
const result = data;
|
const result = data;
|
||||||
|
|
||||||
const model = new MusicReleaseModel({
|
const model = new MusicReleaseModel({
|
||||||
type: 'musicRelease',
|
type: 'musicRelease',
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title,
|
englishTitle: result.title,
|
||||||
year: (new Date(result['first-release-date'])).getFullYear().toString(),
|
year: new Date(result['first-release-date']).getFullYear().toString(),
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
url: '',
|
url: '',
|
||||||
id: result.id,
|
id: result.id,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {MovieModel} from '../../models/MovieModel';
|
||||||
import MediaDbPlugin from '../../main';
|
import MediaDbPlugin from '../../main';
|
||||||
import { SeriesModel } from '../../models/SeriesModel';
|
import { SeriesModel } from '../../models/SeriesModel';
|
||||||
import { GameModel } from '../../models/GameModel';
|
import { GameModel } from '../../models/GameModel';
|
||||||
import {debugLog} from '../../utils/Utils';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
|
||||||
export class OMDbAPI extends APIModel {
|
export class OMDbAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -17,7 +17,7 @@ export class OMDbAPI extends APIModel {
|
||||||
this.apiName = 'OMDbAPI';
|
this.apiName = 'OMDbAPI';
|
||||||
this.apiDescription = 'A free API for Movies, Series and Games.';
|
this.apiDescription = 'A free API for Movies, Series and Games.';
|
||||||
this.apiUrl = 'http://www.omdbapi.com/';
|
this.apiUrl = 'http://www.omdbapi.com/';
|
||||||
this.types = ['movie', 'series'];
|
this.types = [MediaType.Movie, MediaType.Series, MediaType.Game];
|
||||||
this.typeMappings = new Map<string, string>();
|
this.typeMappings = new Map<string, string>();
|
||||||
this.typeMappings.set('movie', 'movie');
|
this.typeMappings.set('movie', 'movie');
|
||||||
this.typeMappings.set('series', 'series');
|
this.typeMappings.set('series', 'series');
|
||||||
|
|
@ -46,9 +46,9 @@ export class OMDbAPI extends APIModel {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog(data.Search);
|
console.debug(data.Search);
|
||||||
|
|
||||||
let ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const result of data.Search) {
|
for (const result of data.Search) {
|
||||||
const type = this.typeMappings.get(result.Type.toLowerCase());
|
const type = this.typeMappings.get(result.Type.toLowerCase());
|
||||||
|
|
@ -56,32 +56,38 @@ export class OMDbAPI extends APIModel {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (type === 'movie') {
|
if (type === 'movie') {
|
||||||
ret.push(new MovieModel({
|
ret.push(
|
||||||
|
new MovieModel({
|
||||||
type: type,
|
type: type,
|
||||||
title: result.Title,
|
title: result.Title,
|
||||||
englishTitle: result.Title,
|
englishTitle: result.Title,
|
||||||
year: result.Year,
|
year: result.Year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.imdbID,
|
id: result.imdbID,
|
||||||
} as MovieModel));
|
} as MovieModel)
|
||||||
|
);
|
||||||
} else if (type === 'series') {
|
} else if (type === 'series') {
|
||||||
ret.push(new SeriesModel({
|
ret.push(
|
||||||
|
new SeriesModel({
|
||||||
type: type,
|
type: type,
|
||||||
title: result.Title,
|
title: result.Title,
|
||||||
englishTitle: result.Title,
|
englishTitle: result.Title,
|
||||||
year: result.Year,
|
year: result.Year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.imdbID,
|
id: result.imdbID,
|
||||||
} as SeriesModel));
|
} as SeriesModel)
|
||||||
|
);
|
||||||
} else if (type === 'game') {
|
} else if (type === 'game') {
|
||||||
ret.push(new GameModel({
|
ret.push(
|
||||||
|
new GameModel({
|
||||||
type: type,
|
type: type,
|
||||||
title: result.Title,
|
title: result.Title,
|
||||||
englishTitle: result.Title,
|
englishTitle: result.Title,
|
||||||
year: result.Year,
|
year: result.Year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.imdbID,
|
id: result.imdbID,
|
||||||
} as GameModel));
|
} as GameModel)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +108,7 @@ export class OMDbAPI extends APIModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await fetchData.json();
|
const result = await fetchData.json();
|
||||||
debugLog(result);
|
console.debug(result);
|
||||||
|
|
||||||
if (result.Response === 'False') {
|
if (result.Response === 'False') {
|
||||||
throw Error(`MDB | Received error from ${this.apiName}: ${result.Error}`);
|
throw Error(`MDB | Received error from ${this.apiName}: ${result.Error}`);
|
||||||
|
|
@ -127,10 +133,12 @@ export class OMDbAPI extends APIModel {
|
||||||
producer: result.Director ?? 'unknown',
|
producer: result.Director ?? 'unknown',
|
||||||
duration: result.Runtime ?? 'unknown',
|
duration: result.Runtime ?? 'unknown',
|
||||||
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
|
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
|
||||||
|
actors: result.Actors?.split(', ') ?? [],
|
||||||
image: result.Poster ?? '',
|
image: result.Poster ?? '',
|
||||||
|
|
||||||
released: true,
|
released: true,
|
||||||
premiere: (new Date(result.Released)).toLocaleDateString() ?? 'unknown',
|
streamingServices: [],
|
||||||
|
premiere: new Date(result.Released).toLocaleDateString() ?? 'unknown',
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
watched: false,
|
watched: false,
|
||||||
|
|
@ -155,11 +163,13 @@ export class OMDbAPI extends APIModel {
|
||||||
episodes: 0,
|
episodes: 0,
|
||||||
duration: result.Runtime ?? 'unknown',
|
duration: result.Runtime ?? 'unknown',
|
||||||
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
|
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
|
||||||
|
actors: result.Actors?.split(', ') ?? [],
|
||||||
image: result.Poster ?? '',
|
image: result.Poster ?? '',
|
||||||
|
|
||||||
released: true,
|
released: true,
|
||||||
|
streamingServices: [],
|
||||||
airing: false,
|
airing: false,
|
||||||
airedFrom: (new Date(result.Released)).toLocaleDateString() ?? 'unknown',
|
airedFrom: new Date(result.Released).toLocaleDateString() ?? 'unknown',
|
||||||
airedTo: 'unknown',
|
airedTo: 'unknown',
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
|
|
@ -185,7 +195,7 @@ export class OMDbAPI extends APIModel {
|
||||||
image: result.Poster ?? '',
|
image: result.Poster ?? '',
|
||||||
|
|
||||||
released: true,
|
released: true,
|
||||||
releaseDate: (new Date(result.Released)).toLocaleDateString() ?? 'unknown',
|
releaseDate: new Date(result.Released).toLocaleDateString() ?? 'unknown',
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
played: false,
|
played: false,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import {APIModel} from '../APIModel';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../../main';
|
import MediaDbPlugin from '../../main';
|
||||||
import { GameModel } from '../../models/GameModel';
|
import { GameModel } from '../../models/GameModel';
|
||||||
import {debugLog} from '../../utils/Utils';
|
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
|
||||||
|
|
@ -17,7 +16,7 @@ export class SteamAPI extends APIModel {
|
||||||
this.apiName = 'SteamAPI';
|
this.apiName = 'SteamAPI';
|
||||||
this.apiDescription = 'A free API for all Steam games.';
|
this.apiDescription = 'A free API for all Steam games.';
|
||||||
this.apiUrl = 'http://www.steampowered.com/';
|
this.apiUrl = 'http://www.steampowered.com/';
|
||||||
this.types = ['games'];
|
this.types = [MediaType.Game];
|
||||||
this.typeMappings = new Map<string, string>();
|
this.typeMappings = new Map<string, string>();
|
||||||
this.typeMappings.set('game', 'game');
|
this.typeMappings.set('game', 'game');
|
||||||
}
|
}
|
||||||
|
|
@ -36,9 +35,9 @@ export class SteamAPI extends APIModel {
|
||||||
|
|
||||||
const data = await fetchData.json;
|
const data = await fetchData.json;
|
||||||
|
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
|
|
||||||
let filteredData = [];
|
const filteredData = [];
|
||||||
|
|
||||||
for (const app of data.applist.apps) {
|
for (const app of data.applist.apps) {
|
||||||
if (app.name.toLowerCase().includes(title.toLowerCase())) {
|
if (app.name.toLowerCase().includes(title.toLowerCase())) {
|
||||||
|
|
@ -49,17 +48,19 @@ export class SteamAPI extends APIModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const result of filteredData) {
|
for (const result of filteredData) {
|
||||||
ret.push(new GameModel({
|
ret.push(
|
||||||
|
new GameModel({
|
||||||
type: MediaType.Game,
|
type: MediaType.Game,
|
||||||
title: result.name,
|
title: result.name,
|
||||||
englishTitle: result.name,
|
englishTitle: result.name,
|
||||||
year: '',
|
year: '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.appid,
|
id: result.appid,
|
||||||
} as GameModel));
|
} as GameModel)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -68,7 +69,7 @@ export class SteamAPI extends APIModel {
|
||||||
async getById(id: string): Promise<MediaTypeModel> {
|
async getById(id: string): Promise<MediaTypeModel> {
|
||||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||||
|
|
||||||
const searchUrl = `http://store.steampowered.com/api/appdetails?appids=${encodeURIComponent(id)}`;
|
const searchUrl = `http://store.steampowered.com/api/appdetails?appids=${encodeURIComponent(id)}&l=en`;
|
||||||
const fetchData = await requestUrl({
|
const fetchData = await requestUrl({
|
||||||
url: searchUrl,
|
url: searchUrl,
|
||||||
});
|
});
|
||||||
|
|
@ -77,7 +78,7 @@ export class SteamAPI extends APIModel {
|
||||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog(await fetchData.json);
|
console.debug(await fetchData.json);
|
||||||
|
|
||||||
let result: any;
|
let result: any;
|
||||||
for (const [key, value] of Object.entries(await fetchData.json)) {
|
for (const [key, value] of Object.entries(await fetchData.json)) {
|
||||||
|
|
@ -92,13 +93,13 @@ export class SteamAPI extends APIModel {
|
||||||
throw Error(`MDB | API returned invalid data.`);
|
throw Error(`MDB | API returned invalid data.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog(result);
|
console.debug(result);
|
||||||
|
|
||||||
const model = new GameModel({
|
const model = new GameModel({
|
||||||
type: MediaType.Game,
|
type: MediaType.Game,
|
||||||
title: result.name,
|
title: result.name,
|
||||||
englishTitle: result.name,
|
englishTitle: result.name,
|
||||||
year: (new Date(result.release_date.date)).getFullYear().toString(),
|
year: new Date(result.release_date.date).getFullYear().toString(),
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
url: `https://store.steampowered.com/app/${result.steam_appid}`,
|
url: `https://store.steampowered.com/app/${result.steam_appid}`,
|
||||||
id: result.steam_appid,
|
id: result.steam_appid,
|
||||||
|
|
@ -108,7 +109,7 @@ export class SteamAPI extends APIModel {
|
||||||
image: result.header_image ?? '',
|
image: result.header_image ?? '',
|
||||||
|
|
||||||
released: !result.release_date?.comming_soon,
|
released: !result.release_date?.comming_soon,
|
||||||
releaseDate: (new Date(result.release_date?.date)).toLocaleDateString() ?? 'unknown',
|
releaseDate: new Date(result.release_date?.date).toLocaleDateString() ?? 'unknown',
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
played: false,
|
played: false,
|
||||||
|
|
@ -117,6 +118,5 @@ export class SteamAPI extends APIModel {
|
||||||
} as GameModel);
|
} as GameModel);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import {APIModel} from '../APIModel';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../../main';
|
import MediaDbPlugin from '../../main';
|
||||||
import { WikiModel } from '../../models/WikiModel';
|
import { WikiModel } from '../../models/WikiModel';
|
||||||
import {debugLog} from '../../utils/Utils';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
|
||||||
export class WikipediaAPI extends APIModel {
|
export class WikipediaAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -14,7 +14,7 @@ export class WikipediaAPI extends APIModel {
|
||||||
this.apiName = 'Wikipedia API';
|
this.apiName = 'Wikipedia API';
|
||||||
this.apiDescription = 'The API behind Wikipedia';
|
this.apiDescription = 'The API behind Wikipedia';
|
||||||
this.apiUrl = 'https://www.wikipedia.com';
|
this.apiUrl = 'https://www.wikipedia.com';
|
||||||
this.types = ['wiki'];
|
this.types = [MediaType.Wiki];
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||||
|
|
@ -22,25 +22,27 @@ export class WikipediaAPI extends APIModel {
|
||||||
|
|
||||||
const searchUrl = `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(title)}&srlimit=20&utf8=&format=json&origin=*`;
|
const searchUrl = `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(title)}&srlimit=20&utf8=&format=json&origin=*`;
|
||||||
const fetchData = await fetch(searchUrl);
|
const fetchData = await fetch(searchUrl);
|
||||||
debugLog(fetchData);
|
console.debug(fetchData);
|
||||||
|
|
||||||
if (fetchData.status !== 200) {
|
if (fetchData.status !== 200) {
|
||||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetchData.json();
|
const data = await fetchData.json();
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
let ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const result of data.query.search) {
|
for (const result of data.query.search) {
|
||||||
ret.push(new WikiModel({
|
ret.push(
|
||||||
|
new WikiModel({
|
||||||
type: 'wiki',
|
type: 'wiki',
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title,
|
englishTitle: result.title,
|
||||||
year: '',
|
year: '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.pageid,
|
id: result.pageid,
|
||||||
} as WikiModel));
|
} as WikiModel)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -57,7 +59,7 @@ export class WikipediaAPI extends APIModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetchData.json();
|
const data = await fetchData.json();
|
||||||
debugLog(data);
|
console.debug(data);
|
||||||
const result: any = Object.entries(data?.query?.pages)[0][1];
|
const result: any = Object.entries(data?.query?.pages)[0][1];
|
||||||
|
|
||||||
const model = new WikiModel({
|
const model = new WikiModel({
|
||||||
|
|
@ -70,7 +72,7 @@ export class WikipediaAPI extends APIModel {
|
||||||
id: result.pageid,
|
id: result.pageid,
|
||||||
|
|
||||||
wikiUrl: result.fullurl,
|
wikiUrl: result.fullurl,
|
||||||
lastUpdated: (new Date(result.touched)).toLocaleDateString() ?? 'unknown',
|
lastUpdated: new Date(result.touched).toLocaleDateString() ?? 'unknown',
|
||||||
length: result.length,
|
length: result.length,
|
||||||
|
|
||||||
userData: {},
|
userData: {},
|
||||||
|
|
|
||||||
170
src/main.ts
170
src/main.ts
|
|
@ -2,19 +2,19 @@ import {MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder}
|
||||||
import { getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab } from './settings/Settings';
|
import { getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab } from './settings/Settings';
|
||||||
import { APIManager } from './api/APIManager';
|
import { APIManager } from './api/APIManager';
|
||||||
import { MediaTypeModel } from './models/MediaTypeModel';
|
import { MediaTypeModel } from './models/MediaTypeModel';
|
||||||
import {CreateNoteOptions, dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString} from './utils/Utils';
|
import { CreateNoteOptions, dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, unCamelCase } from './utils/Utils';
|
||||||
import { OMDbAPI } from './api/apis/OMDbAPI';
|
import { OMDbAPI } from './api/apis/OMDbAPI';
|
||||||
import { MALAPI } from './api/apis/MALAPI';
|
import { MALAPI } from './api/apis/MALAPI';
|
||||||
import { WikipediaAPI } from './api/apis/WikipediaAPI';
|
import { WikipediaAPI } from './api/apis/WikipediaAPI';
|
||||||
import { MusicBrainzAPI } from './api/apis/MusicBrainzAPI';
|
import { MusicBrainzAPI } from './api/apis/MusicBrainzAPI';
|
||||||
import {MediaTypeManager} from './utils/MediaTypeManager';
|
import { MEDIA_TYPES, MediaTypeManager } from './utils/MediaTypeManager';
|
||||||
import { SteamAPI } from './api/apis/SteamAPI';
|
import { SteamAPI } from './api/apis/SteamAPI';
|
||||||
import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI';
|
import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI';
|
||||||
import { PropertyMapper } from './settings/PropertyMapper';
|
import { PropertyMapper } from './settings/PropertyMapper';
|
||||||
import { YAMLConverter } from './utils/YAMLConverter';
|
import { YAMLConverter } from './utils/YAMLConverter';
|
||||||
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
||||||
import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping';
|
import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping';
|
||||||
import {ModalHelper, ModalResultCode} from './utils/ModalHelper';
|
import { ModalHelper, ModalResultCode, SearchModalOptions } from './utils/ModalHelper';
|
||||||
|
|
||||||
export default class MediaDbPlugin extends Plugin {
|
export default class MediaDbPlugin extends Plugin {
|
||||||
settings: MediaDbPluginSettings;
|
settings: MediaDbPluginSettings;
|
||||||
|
|
@ -25,7 +25,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
|
|
||||||
frontMatterRexExpPattern: string = '^(---)\\n[\\s\\S]*?\\n---';
|
frontMatterRexExpPattern: string = '^(---)\\n[\\s\\S]*?\\n---';
|
||||||
|
|
||||||
async onload() {
|
async onload(): Promise<void> {
|
||||||
this.apiManager = new APIManager();
|
this.apiManager = new APIManager();
|
||||||
// register APIs
|
// register APIs
|
||||||
this.apiManager.registerAPI(new OMDbAPI(this));
|
this.apiManager.registerAPI(new OMDbAPI(this));
|
||||||
|
|
@ -45,14 +45,14 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
this.addSettingTab(new MediaDbSettingTab(this.app, this));
|
this.addSettingTab(new MediaDbSettingTab(this.app, this));
|
||||||
|
|
||||||
this.mediaTypeManager.updateTemplates(this.settings);
|
this.mediaTypeManager.updateTemplates(this.settings);
|
||||||
|
this.mediaTypeManager.updateFolders(this.settings);
|
||||||
|
|
||||||
// add icon to the left ribbon
|
// add icon to the left ribbon
|
||||||
const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', (evt: MouseEvent) =>
|
const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', () => this.createEntryWithAdvancedSearchModal());
|
||||||
this.createEntryWithAdvancedSearchModal(),
|
|
||||||
);
|
|
||||||
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
||||||
|
|
||||||
this.registerEvent(this.app.workspace.on('file-menu', (menu, file) => {
|
this.registerEvent(
|
||||||
|
this.app.workspace.on('file-menu', (menu, file) => {
|
||||||
if (file instanceof TFolder) {
|
if (file instanceof TFolder) {
|
||||||
menu.addItem(item => {
|
menu.addItem(item => {
|
||||||
item.setTitle('Import folder as Media DB entries')
|
item.setTitle('Import folder as Media DB entries')
|
||||||
|
|
@ -60,18 +60,31 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
.onClick(() => this.createEntriesFromFolder(file));
|
.onClick(() => this.createEntriesFromFolder(file));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// register command to open search modal
|
// register command to open search modal
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'open-media-db-search-modal',
|
id: 'open-media-db-search-modal',
|
||||||
name: 'Add new Media DB entry',
|
name: 'Create Media DB entry',
|
||||||
|
callback: () => this.createEntryWithSearchModal(),
|
||||||
|
});
|
||||||
|
for (const mediaType of MEDIA_TYPES) {
|
||||||
|
this.addCommand({
|
||||||
|
id: `open-media-db-search-modal-with-${mediaType}`,
|
||||||
|
name: `Create Media DB entry (${unCamelCase(mediaType)})`,
|
||||||
|
callback: () => this.createEntryWithSearchModal({ preselectedTypes: [mediaType] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.addCommand({
|
||||||
|
id: 'open-media-db-advanced-search-modal',
|
||||||
|
name: 'Create Media DB entry (advanced search)',
|
||||||
callback: () => this.createEntryWithAdvancedSearchModal(),
|
callback: () => this.createEntryWithAdvancedSearchModal(),
|
||||||
});
|
});
|
||||||
// register command to open id search modal
|
// register command to open id search modal
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'open-media-db-id-search-modal',
|
id: 'open-media-db-id-search-modal',
|
||||||
name: 'Add new Media DB entry by id',
|
name: 'Create Media DB entry by id',
|
||||||
callback: () => this.createEntryWithIdSearchModal(),
|
callback: () => this.createEntryWithIdSearchModal(),
|
||||||
});
|
});
|
||||||
// register command to update the open note
|
// register command to update the open note
|
||||||
|
|
@ -123,9 +136,8 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
* - replace the detail query
|
* - replace the detail query
|
||||||
* - maybe custom link syntax
|
* - maybe custom link syntax
|
||||||
*/
|
*/
|
||||||
async createLinkWithSearchModal() {
|
async createLinkWithSearchModal(): Promise<void> {
|
||||||
|
const apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
||||||
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async (advancedSearchModalData) => {
|
|
||||||
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -133,7 +145,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectResults: MediaTypeModel[] = await this.modalHelper.openSelectModal({elements: apiSearchResults, multiSelect: false}, async (selectModalData) => {
|
const selectResults: MediaTypeModel[] = await this.modalHelper.openSelectModal({ elements: apiSearchResults, multiSelect: false }, async selectModalData => {
|
||||||
return await this.queryDetails(selectModalData.selected);
|
return await this.queryDetails(selectModalData.selected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -151,15 +163,43 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntryWithSearchModal() {
|
async createEntryWithSearchModal(searchModalOptions?: SearchModalOptions): Promise<void> {
|
||||||
|
let types: string[] = [];
|
||||||
|
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openSearchModal(searchModalOptions ?? {}, async searchModalData => {
|
||||||
|
types = searchModalData.types;
|
||||||
|
const apis = this.apiManager.apis.filter(x => x.hasTypeOverlap(searchModalData.types)).map(x => x.apiName);
|
||||||
|
return await this.apiManager.query(searchModalData.query, apis);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!apiSearchResults) {
|
||||||
|
// TODO: add new notice saying no results found?
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// filter the results
|
||||||
* TODO: further refactor: extract it into own method, pass the action (api query) as lambda as well as an options object
|
apiSearchResults = apiSearchResults.filter(x => types.contains(x.type));
|
||||||
*/
|
|
||||||
async createEntryWithAdvancedSearchModal() {
|
let selectResults: MediaTypeModel[];
|
||||||
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async (advancedSearchModalData) => {
|
let proceed: boolean;
|
||||||
|
|
||||||
|
while (!proceed) {
|
||||||
|
selectResults = await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
|
||||||
|
return await this.queryDetails(selectModalData.selected);
|
||||||
|
});
|
||||||
|
if (!selectResults) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
proceed = await this.modalHelper.openPreviewModal({ elements: selectResults }, async previewModalData => {
|
||||||
|
return previewModalData.confirmed;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.createMediaDbNotes(selectResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createEntryWithAdvancedSearchModal(): Promise<void> {
|
||||||
|
const apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
||||||
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -172,14 +212,14 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
let proceed: boolean;
|
let proceed: boolean;
|
||||||
|
|
||||||
while (!proceed) {
|
while (!proceed) {
|
||||||
selectResults = await this.modalHelper.openSelectModal({elements: apiSearchResults}, async (selectModalData) => {
|
selectResults = await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
|
||||||
return await this.queryDetails(selectModalData.selected);
|
return await this.queryDetails(selectModalData.selected);
|
||||||
});
|
});
|
||||||
if (!selectResults) {
|
if (!selectResults) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
proceed = await this.modalHelper.openPreviewModal({elements: selectResults}, async (previewModalData) => {
|
proceed = await this.modalHelper.openPreviewModal({ elements: selectResults }, async previewModalData => {
|
||||||
return previewModalData.confirmed;
|
return previewModalData.confirmed;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -192,14 +232,14 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
let proceed: boolean;
|
let proceed: boolean;
|
||||||
|
|
||||||
while (!proceed) {
|
while (!proceed) {
|
||||||
idSearchResult = await this.modalHelper.openIdSearchModal({}, async (idSearchModalData) => {
|
idSearchResult = await this.modalHelper.openIdSearchModal({}, async idSearchModalData => {
|
||||||
return await this.apiManager.queryDetailedInfoById(idSearchModalData.query, idSearchModalData.api);
|
return await this.apiManager.queryDetailedInfoById(idSearchModalData.query, idSearchModalData.api);
|
||||||
});
|
});
|
||||||
if (!idSearchResult) {
|
if (!idSearchResult) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
proceed = await this.modalHelper.openPreviewModal({elements: [idSearchResult]}, async (previewModalData) => {
|
proceed = await this.modalHelper.openPreviewModal({ elements: [idSearchResult] }, async previewModalData => {
|
||||||
return previewModalData.confirmed;
|
return previewModalData.confirmed;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +254,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
async queryDetails(models: MediaTypeModel[]): Promise<MediaTypeModel[]> {
|
async queryDetails(models: MediaTypeModel[]): Promise<MediaTypeModel[]> {
|
||||||
let detailModels: MediaTypeModel[] = [];
|
const detailModels: MediaTypeModel[] = [];
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
try {
|
try {
|
||||||
detailModels.push(await this.apiManager.queryDetailedInfo(model));
|
detailModels.push(await this.apiManager.queryDetailedInfo(model));
|
||||||
|
|
@ -230,7 +270,11 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
try {
|
try {
|
||||||
console.debug('MDB | creating new note');
|
console.debug('MDB | creating new note');
|
||||||
|
|
||||||
let fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, options);
|
const fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, options);
|
||||||
|
|
||||||
|
if (!options.folder) {
|
||||||
|
options.folder = await this.mediaTypeManager.getFolder(mediaTypeModel, this.app);
|
||||||
|
}
|
||||||
|
|
||||||
await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options);
|
await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -239,7 +283,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions) {
|
async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise<string> {
|
||||||
let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
|
let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
|
||||||
let fileContent = '';
|
let fileContent = '';
|
||||||
const template = options.attachTemplate ? await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app) : '';
|
const template = options.attachTemplate ? await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app) : '';
|
||||||
|
|
@ -251,12 +295,12 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
return fileContent;
|
return fileContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
async attachFile(fileMetadata: any, fileContent: string, fileToAttach?: TFile): Promise<{ fileMetadata: any, fileContent: string }> {
|
async attachFile(fileMetadata: any, fileContent: string, fileToAttach?: TFile): Promise<{ fileMetadata: any; fileContent: string }> {
|
||||||
if (!fileToAttach) {
|
if (!fileToAttach) {
|
||||||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||||
}
|
}
|
||||||
|
|
||||||
let attachFileMetadata: any = this.getMetadataFromFileCache(fileToAttach);
|
const attachFileMetadata: any = this.getMetadataFromFileCache(fileToAttach);
|
||||||
fileMetadata = Object.assign(attachFileMetadata, fileMetadata);
|
fileMetadata = Object.assign(attachFileMetadata, fileMetadata);
|
||||||
|
|
||||||
let attachFileContent: string = await this.app.vault.read(fileToAttach);
|
let attachFileContent: string = await this.app.vault.read(fileToAttach);
|
||||||
|
|
@ -268,12 +312,12 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||||
}
|
}
|
||||||
|
|
||||||
async attachTemplate(fileMetadata: any, fileContent: string, template: string): Promise<{ fileMetadata: any, fileContent: string }> {
|
async attachTemplate(fileMetadata: any, fileContent: string, template: string): Promise<{ fileMetadata: any; fileContent: string }> {
|
||||||
if (!template) {
|
if (!template) {
|
||||||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||||
}
|
}
|
||||||
|
|
||||||
let templateMetadata: any = this.getMetaDataFromFileContent(template);
|
const templateMetadata: any = this.getMetaDataFromFileContent(template);
|
||||||
fileMetadata = Object.assign(templateMetadata, fileMetadata);
|
fileMetadata = Object.assign(templateMetadata, fileMetadata);
|
||||||
|
|
||||||
const regExp = new RegExp(this.frontMatterRexExpPattern);
|
const regExp = new RegExp(this.frontMatterRexExpPattern);
|
||||||
|
|
@ -309,7 +353,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetadataFromFileCache(file: TFile) {
|
getMetadataFromFileCache(file: TFile): any {
|
||||||
let metadata: any = this.app.metadataCache.getFileCache(file).frontmatter;
|
let metadata: any = this.app.metadataCache.getFileCache(file).frontmatter;
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
metadata = Object.assign({}, metadata); // copy
|
metadata = Object.assign({}, metadata); // copy
|
||||||
|
|
@ -327,12 +371,9 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
* @param fileContent
|
* @param fileContent
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions) {
|
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions): Promise<void> {
|
||||||
// find and possibly create the folder set in settings or passed in folder
|
// find and possibly create the folder set in settings or passed in folder
|
||||||
const folder = options.folder ?? this.app.vault.getAbstractFileByPath(this.settings.folder);
|
const folder = options.folder ?? this.app.vault.getAbstractFileByPath('/');
|
||||||
if (!folder) {
|
|
||||||
await this.app.vault.createFolder(folder.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
fileName = replaceIllegalFileNameCharactersInString(fileName);
|
fileName = replaceIllegalFileNameCharactersInString(fileName);
|
||||||
const filePath = `${folder.path}/${fileName}.md`;
|
const filePath = `${folder.path}/${fileName}.md`;
|
||||||
|
|
@ -362,7 +403,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
* Update the active note by querying the API again.
|
* Update the active note by querying the API again.
|
||||||
* Tries to read the type, id and dataSource of the active note. If successful it will query the api, delete the old note and create a new one.
|
* Tries to read the type, id and dataSource of the active note. If successful it will query the api, delete the old note and create a new one.
|
||||||
*/
|
*/
|
||||||
async updateActiveNote(onlyMetadata: boolean = false) {
|
async updateActiveNote(onlyMetadata: boolean = false): Promise<void> {
|
||||||
const activeFile: TFile = this.app.workspace.getActiveFile();
|
const activeFile: TFile = this.app.workspace.getActiveFile();
|
||||||
if (!activeFile) {
|
if (!activeFile) {
|
||||||
throw new Error('MDB | there is no active note');
|
throw new Error('MDB | there is no active note');
|
||||||
|
|
@ -377,7 +418,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
throw new Error('MDB | active note is not a Media DB entry or is missing metadata');
|
throw new Error('MDB | active note is not a Media DB entry or is missing metadata');
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldMediaTypeModel = this.mediaTypeManager.createMediaTypeModelFromMediaType(metadata, metadata.type);
|
const oldMediaTypeModel = this.mediaTypeManager.createMediaTypeModelFromMediaType(metadata, metadata.type);
|
||||||
// console.debug(oldMediaTypeModel);
|
// console.debug(oldMediaTypeModel);
|
||||||
|
|
||||||
let newMediaTypeModel = await this.apiManager.queryDetailedInfoById(metadata.id, metadata.dataSource);
|
let newMediaTypeModel = await this.apiManager.queryDetailedInfoById(metadata.id, metadata.dataSource);
|
||||||
|
|
@ -395,17 +436,16 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
} else {
|
} else {
|
||||||
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachTemplate: true, folder: activeFile.parent, openNote: true });
|
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachTemplate: true, folder: activeFile.parent, openNote: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntriesFromFolder(folder: TFolder) {
|
async createEntriesFromFolder(folder: TFolder): Promise<void> {
|
||||||
const erroredFiles: { filePath: string, error: string }[] = [];
|
const erroredFiles: { filePath: string; error: string }[] = [];
|
||||||
let canceled: boolean = false;
|
let canceled: boolean = false;
|
||||||
|
|
||||||
const {selectedAPI, titleFieldName, appendContent} = await new Promise<{ selectedAPI: string, titleFieldName: string, appendContent: boolean }>((resolve, reject) => {
|
const { selectedAPI, titleFieldName, appendContent } = await new Promise<{ selectedAPI: string; titleFieldName: string; appendContent: boolean }>(resolve => {
|
||||||
new MediaDbFolderImportModal(this.app, this, ((selectedAPI: string, titleFieldName: string, appendContent: boolean) => {
|
new MediaDbFolderImportModal(this.app, this, (selectedAPI: string, titleFieldName: string, appendContent: boolean) => {
|
||||||
resolve({ selectedAPI, titleFieldName, appendContent });
|
resolve({ selectedAPI, titleFieldName, appendContent });
|
||||||
})).open();
|
}).open();
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const child of folder.children) {
|
for (const child of folder.children) {
|
||||||
|
|
@ -416,11 +456,11 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata: any = this.getMetadataFromFileCache(file);
|
const metadata: any = this.getMetadataFromFileCache(file);
|
||||||
|
|
||||||
let title = metadata[titleFieldName];
|
const title = metadata[titleFieldName];
|
||||||
if (!title) {
|
if (!title) {
|
||||||
erroredFiles.push({filePath: file.path, error: `metadata field \'${titleFieldName}\' not found or empty`});
|
erroredFiles.push({ filePath: file.path, error: `metadata field '${titleFieldName}' not found or empty` });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -436,7 +476,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {selectModalResult, selectModal} = await this.modalHelper.createSelectModal({elements: results, skipButton: true, modalTitle: `Results for \'${title}\'`});
|
const { selectModalResult, selectModal } = await this.modalHelper.createSelectModal({ elements: results, skipButton: true, modalTitle: `Results for '${title}'` });
|
||||||
|
|
||||||
if (selectModalResult.code === ModalResultCode.ERROR) {
|
if (selectModalResult.code === ModalResultCode.ERROR) {
|
||||||
erroredFiles.push({ filePath: file.path, error: selectModalResult.error.message });
|
erroredFiles.push({ filePath: file.path, error: selectModalResult.error.message });
|
||||||
|
|
@ -474,34 +514,35 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createErroredFilesReport(erroredFiles: { filePath: string, error: string }[]): Promise<void> {
|
async createErroredFilesReport(erroredFiles: { filePath: string; error: string }[]): Promise<void> {
|
||||||
const title = `bulk import error report ${dateTimeToString(new Date())}`;
|
const title = `MDB - bulk import error report ${dateTimeToString(new Date())}`;
|
||||||
const filePath = `${this.settings.folder.replace(/\/$/, '')}/${title}.md`;
|
const filePath = `${title}.md`;
|
||||||
|
|
||||||
const table = [['file', 'error']].concat(erroredFiles.map(x => [x.filePath, x.error]));
|
const table = [['file', 'error']].concat(erroredFiles.map(x => [x.filePath, x.error]));
|
||||||
|
|
||||||
let fileContent = `# ${title}\n\n${markdownTable(table)}`;
|
const fileContent = `# ${title}\n\n${markdownTable(table)}`;
|
||||||
|
await this.app.vault.create(filePath, fileContent);
|
||||||
const targetFile = await this.app.vault.create(filePath, fileContent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings(): Promise<void> {
|
||||||
// console.log(DEFAULT_SETTINGS);
|
// console.log(DEFAULT_SETTINGS);
|
||||||
const diskSettings: MediaDbPluginSettings = await this.loadData();
|
const diskSettings: MediaDbPluginSettings = await this.loadData();
|
||||||
const defaultSettings: MediaDbPluginSettings = getDefaultSettings(this);
|
const defaultSettings: MediaDbPluginSettings = getDefaultSettings(this);
|
||||||
const loadedSettings: MediaDbPluginSettings = Object.assign({}, defaultSettings, diskSettings);
|
const loadedSettings: MediaDbPluginSettings = Object.assign({}, defaultSettings, diskSettings);
|
||||||
|
|
||||||
// migrate the settings loaded from the disk to match the structure of the default settings
|
// migrate the settings loaded from the disk to match the structure of the default settings
|
||||||
let newPropertyMappings: PropertyMappingModel[] = [];
|
const newPropertyMappings: PropertyMappingModel[] = [];
|
||||||
for (const defaultPropertyMappingModel of defaultSettings.propertyMappingModels) {
|
for (const defaultPropertyMappingModel of defaultSettings.propertyMappingModels) {
|
||||||
let newPropertyMappingModel: PropertyMappingModel = loadedSettings.propertyMappingModels.find(x => x.type === defaultPropertyMappingModel.type);
|
const newPropertyMappingModel: PropertyMappingModel = loadedSettings.propertyMappingModels.find(x => x.type === defaultPropertyMappingModel.type);
|
||||||
if (newPropertyMappingModel === undefined) { // if the propertyMappingModel exists in the default settings but not the loaded settings, add it
|
if (newPropertyMappingModel === undefined) {
|
||||||
|
// if the propertyMappingModel exists in the default settings but not the loaded settings, add it
|
||||||
newPropertyMappings.push(defaultPropertyMappingModel);
|
newPropertyMappings.push(defaultPropertyMappingModel);
|
||||||
} else { // if the propertyMappingModel also exists in the loaded settings, add it from there
|
} else {
|
||||||
let newProperties: PropertyMapping[] = [];
|
// if the propertyMappingModel also exists in the loaded settings, add it from there
|
||||||
|
const newProperties: PropertyMapping[] = [];
|
||||||
|
|
||||||
for (const defaultProperty of defaultPropertyMappingModel.properties) {
|
for (const defaultProperty of defaultPropertyMappingModel.properties) {
|
||||||
let newProperty = newPropertyMappingModel.properties.find(x => x.property === defaultProperty.property);
|
const newProperty = newPropertyMappingModel.properties.find(x => x.property === defaultProperty.property);
|
||||||
if (newProperty === undefined) {
|
if (newProperty === undefined) {
|
||||||
// default property is an instance
|
// default property is an instance
|
||||||
newProperties.push(defaultProperty);
|
newProperties.push(defaultProperty);
|
||||||
|
|
@ -519,8 +560,9 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
this.settings = loadedSettings;
|
this.settings = loadedSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveSettings() {
|
async saveSettings(): Promise<void> {
|
||||||
this.mediaTypeManager.updateTemplates(this.settings);
|
this.mediaTypeManager.updateTemplates(this.settings);
|
||||||
|
this.mediaTypeManager.updateFolders(this.settings);
|
||||||
|
|
||||||
await this.saveData(this.settings);
|
await this.saveData(this.settings);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,13 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
query: string;
|
query: string;
|
||||||
isBusy: boolean;
|
isBusy: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
selectedApis: { name: string, selected: boolean }[];
|
selectedApis: { name: string; selected: boolean }[];
|
||||||
|
|
||||||
searchBtn: ButtonComponent;
|
searchBtn: ButtonComponent;
|
||||||
|
|
||||||
submitCallback?: (res: AdvancedSearchModalData) => void;
|
submitCallback?: (res: AdvancedSearchModalData) => void;
|
||||||
closeCallback?: (err?: Error) => void;
|
closeCallback?: (err?: Error) => void;
|
||||||
|
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin, advancedSearchModalOptions: AdvancedSearchModalOptions) {
|
constructor(plugin: MediaDbPlugin, advancedSearchModalOptions: AdvancedSearchModalOptions) {
|
||||||
advancedSearchModalOptions = Object.assign({}, ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS, advancedSearchModalOptions);
|
advancedSearchModalOptions = Object.assign({}, ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS, advancedSearchModalOptions);
|
||||||
super(plugin.app);
|
super(plugin.app);
|
||||||
|
|
@ -39,7 +38,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
this.closeCallback = closeCallback;
|
this.closeCallback = closeCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPressCallback(event: KeyboardEvent) {
|
keyPressCallback(event: KeyboardEvent): void {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +66,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpen() {
|
onOpen(): void {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
|
|
||||||
contentEl.createEl('h2', { text: this.title });
|
contentEl.createEl('h2', { text: this.title });
|
||||||
|
|
@ -99,7 +98,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
||||||
apiToggleComponent.setTooltip(api.apiName);
|
apiToggleComponent.setTooltip(api.apiName);
|
||||||
apiToggleComponent.setValue(this.selectedApis.find(x => x.name === api.apiName).selected);
|
apiToggleComponent.setValue(this.selectedApis.find(x => x.name === api.apiName).selected);
|
||||||
apiToggleComponent.onChange((value) => {
|
apiToggleComponent.onChange(value => {
|
||||||
this.selectedApis.find(x => x.name === api.apiName).selected = value;
|
this.selectedApis.find(x => x.name === api.apiName).selected = value;
|
||||||
});
|
});
|
||||||
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
||||||
|
|
@ -124,10 +123,9 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback();
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
this.selectedApi = plugin.apiManager.apis[0].apiName;
|
this.selectedApi = plugin.apiManager.apis[0].apiName;
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit(): void {
|
||||||
this.onSubmit(this.selectedApi, this.titleFieldName, this.appendContent);
|
this.onSubmit(this.selectedApi, this.titleFieldName, this.appendContent);
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpen() {
|
onOpen(): void {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
|
|
||||||
contentEl.createEl('h2', { text: 'Import folder as Media DB entries' });
|
contentEl.createEl('h2', { text: 'Import folder as Media DB entries' });
|
||||||
|
|
@ -39,7 +39,6 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
}
|
}
|
||||||
apiSelectorWrapper.appendChild(apiSelectorComponent.selectEl);
|
apiSelectorWrapper.appendChild(apiSelectorComponent.selectEl);
|
||||||
|
|
||||||
|
|
||||||
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
|
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
|
||||||
contentEl.createEl('h3', { text: 'Append note content to Media DB entry.' });
|
contentEl.createEl('h3', { text: 'Append note content to Media DB entry.' });
|
||||||
|
|
||||||
|
|
@ -54,10 +53,9 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
|
|
||||||
const appendContentToggle = new ToggleComponent(appendContentToggleElementWrapper);
|
const appendContentToggle = new ToggleComponent(appendContentToggleElementWrapper);
|
||||||
appendContentToggle.setValue(false);
|
appendContentToggle.setValue(false);
|
||||||
appendContentToggle.onChange(value => this.appendContent = value);
|
appendContentToggle.onChange(value => (this.appendContent = value));
|
||||||
appendContentToggleComponentWrapper.appendChild(appendContentToggle.toggleEl);
|
appendContentToggleComponentWrapper.appendChild(appendContentToggle.toggleEl);
|
||||||
|
|
||||||
|
|
||||||
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
|
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
|
||||||
contentEl.createEl('h3', { text: 'The name of the metadata field that should be used as the title to query.' });
|
contentEl.createEl('h3', { text: 'The name of the metadata field that should be used as the title to query.' });
|
||||||
|
|
||||||
|
|
@ -65,8 +63,8 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
const titleFieldNameComponent = new TextComponent(contentEl);
|
const titleFieldNameComponent = new TextComponent(contentEl);
|
||||||
titleFieldNameComponent.inputEl.style.width = '100%';
|
titleFieldNameComponent.inputEl.style.width = '100%';
|
||||||
titleFieldNameComponent.setPlaceholder(placeholder);
|
titleFieldNameComponent.setPlaceholder(placeholder);
|
||||||
titleFieldNameComponent.onChange(value => this.titleFieldName = value);
|
titleFieldNameComponent.onChange(value => (this.titleFieldName = value));
|
||||||
titleFieldNameComponent.inputEl.addEventListener('keydown', (ke) => {
|
titleFieldNameComponent.inputEl.addEventListener('keydown', ke => {
|
||||||
if (ke.key === 'Enter') {
|
if (ke.key === 'Enter') {
|
||||||
this.submit();
|
this.submit();
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +90,7 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose(): void {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
submitCallback?: (res: IdSearchModalData, err?: Error) => void;
|
submitCallback?: (res: IdSearchModalData, err?: Error) => void;
|
||||||
closeCallback?: (err?: Error) => void;
|
closeCallback?: (err?: Error) => void;
|
||||||
|
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin, idSearchModalOptions: IdSearchModalOptions) {
|
constructor(plugin: MediaDbPlugin, idSearchModalOptions: IdSearchModalOptions) {
|
||||||
idSearchModalOptions = Object.assign({}, ID_SEARCH_MODAL_DEFAULT_OPTIONS, idSearchModalOptions);
|
idSearchModalOptions = Object.assign({}, ID_SEARCH_MODAL_DEFAULT_OPTIONS, idSearchModalOptions);
|
||||||
super(plugin.app);
|
super(plugin.app);
|
||||||
|
|
@ -34,7 +33,7 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
this.closeCallback = closeCallback;
|
this.closeCallback = closeCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPressCallback(event: KeyboardEvent) {
|
keyPressCallback(event: KeyboardEvent): void {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +59,7 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpen() {
|
onOpen(): void {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
|
|
||||||
contentEl.createEl('h2', { text: this.title });
|
contentEl.createEl('h2', { text: this.title });
|
||||||
|
|
@ -109,10 +108,9 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback();
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,14 @@ export class MediaDbPreviewModal extends Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
async preview(): Promise<void> {
|
async preview(): Promise<void> {
|
||||||
let {contentEl} = this;
|
const { contentEl } = this;
|
||||||
contentEl.addClass('media-db-plugin-preview-modal');
|
contentEl.addClass('media-db-plugin-preview-modal');
|
||||||
|
|
||||||
contentEl.createEl('h2', { text: this.title });
|
contentEl.createEl('h2', { text: this.title });
|
||||||
|
|
||||||
const previewWrapper = contentEl.createDiv({ cls: 'media-db-plugin-preview-wrapper' });
|
const previewWrapper = contentEl.createDiv({ cls: 'media-db-plugin-preview-wrapper' });
|
||||||
|
|
||||||
for (let result of this.elements) {
|
for (const result of this.elements) {
|
||||||
previewWrapper.createEl('h3', { text: result.englishTitle });
|
previewWrapper.createEl('h3', { text: result.englishTitle });
|
||||||
const fileDiv = previewWrapper.createDiv();
|
const fileDiv = previewWrapper.createDiv();
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ export class MediaDbPreviewModal extends Modal {
|
||||||
const bottomSettingRow = new Setting(contentEl);
|
const bottomSettingRow = new Setting(contentEl);
|
||||||
bottomSettingRow.addButton(btn => {
|
bottomSettingRow.addButton(btn => {
|
||||||
btn.setButtonText('Cancel');
|
btn.setButtonText('Cancel');
|
||||||
btn.onClick(() => this.closeCallback());
|
btn.onClick(() => this.close());
|
||||||
btn.buttonEl.addClass('media-db-plugin-button');
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
this.cancelButton = btn;
|
this.cancelButton = btn;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
132
src/modals/MediaDbSearchModal.ts
Normal file
132
src/modals/MediaDbSearchModal.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
import { ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
||||||
|
import { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import MediaDbPlugin from '../main';
|
||||||
|
import { SEARCH_MODAL_DEFAULT_OPTIONS, SearchModalData, SearchModalOptions } from '../utils/ModalHelper';
|
||||||
|
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||||
|
import { unCamelCase } from '../utils/Utils';
|
||||||
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
|
export class MediaDbSearchModal extends Modal {
|
||||||
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
|
query: string;
|
||||||
|
isBusy: boolean;
|
||||||
|
title: string;
|
||||||
|
selectedTypes: { name: MediaType; selected: boolean }[];
|
||||||
|
|
||||||
|
searchBtn: ButtonComponent;
|
||||||
|
|
||||||
|
submitCallback?: (res: SearchModalData) => void;
|
||||||
|
closeCallback?: (err?: Error) => void;
|
||||||
|
|
||||||
|
constructor(plugin: MediaDbPlugin, searchModalOptions: SearchModalOptions) {
|
||||||
|
searchModalOptions = Object.assign({}, SEARCH_MODAL_DEFAULT_OPTIONS, searchModalOptions);
|
||||||
|
super(plugin.app);
|
||||||
|
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.selectedTypes = [];
|
||||||
|
this.title = searchModalOptions.modalTitle;
|
||||||
|
this.query = searchModalOptions.prefilledSearchString;
|
||||||
|
|
||||||
|
for (const mediaType of MEDIA_TYPES) {
|
||||||
|
this.selectedTypes.push({ name: mediaType, selected: searchModalOptions.preselectedTypes.contains(mediaType) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSubmitCallback(submitCallback: (res: SearchModalData) => void): void {
|
||||||
|
this.submitCallback = submitCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCloseCallback(closeCallback: (err?: Error) => void): void {
|
||||||
|
this.closeCallback = closeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPressCallback(event: KeyboardEvent): void {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(): Promise<MediaTypeModel[]> {
|
||||||
|
if (!this.query || this.query.length < 3) {
|
||||||
|
new Notice('MDB | Query too short');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const types: MediaType[] = this.selectedTypes.filter(x => x.selected).map(x => x.name);
|
||||||
|
|
||||||
|
if (types.length === 0) {
|
||||||
|
new Notice('MDB | No Type selected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isBusy) {
|
||||||
|
this.isBusy = true;
|
||||||
|
this.searchBtn.setDisabled(false);
|
||||||
|
this.searchBtn.setButtonText('Searching...');
|
||||||
|
|
||||||
|
this.submitCallback({ query: this.query, types: types });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen(): void {
|
||||||
|
const { contentEl } = this;
|
||||||
|
|
||||||
|
contentEl.createEl('h2', { text: this.title });
|
||||||
|
|
||||||
|
const placeholder = 'Search by title';
|
||||||
|
const searchComponent = new TextComponent(contentEl);
|
||||||
|
searchComponent.inputEl.style.width = '100%';
|
||||||
|
searchComponent.setPlaceholder(placeholder);
|
||||||
|
searchComponent.setValue(this.query);
|
||||||
|
searchComponent.onChange(value => (this.query = value));
|
||||||
|
searchComponent.inputEl.addEventListener('keydown', this.keyPressCallback.bind(this));
|
||||||
|
|
||||||
|
contentEl.appendChild(searchComponent.inputEl);
|
||||||
|
searchComponent.inputEl.focus();
|
||||||
|
|
||||||
|
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
|
||||||
|
contentEl.createEl('h3', { text: 'APIs to search' });
|
||||||
|
|
||||||
|
for (const mediaType of MEDIA_TYPES) {
|
||||||
|
const apiToggleListElementWrapper = contentEl.createEl('div', { cls: 'media-db-plugin-list-wrapper' });
|
||||||
|
|
||||||
|
const apiToggleTextWrapper = apiToggleListElementWrapper.createEl('div', { cls: 'media-db-plugin-list-text-wrapper' });
|
||||||
|
apiToggleTextWrapper.createEl('span', { text: unCamelCase(mediaType), cls: 'media-db-plugin-list-text' });
|
||||||
|
|
||||||
|
const apiToggleComponentWrapper = apiToggleListElementWrapper.createEl('div', { cls: 'media-db-plugin-list-toggle' });
|
||||||
|
|
||||||
|
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
||||||
|
apiToggleComponent.setTooltip(unCamelCase(mediaType));
|
||||||
|
apiToggleComponent.setValue(this.selectedTypes.find(x => x.name === mediaType).selected);
|
||||||
|
apiToggleComponent.onChange(value => {
|
||||||
|
this.selectedTypes.find(x => x.name === mediaType).selected = value;
|
||||||
|
});
|
||||||
|
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
|
||||||
|
|
||||||
|
new Setting(contentEl)
|
||||||
|
.addButton(btn => {
|
||||||
|
btn.setButtonText('Cancel');
|
||||||
|
btn.onClick(() => this.close());
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
})
|
||||||
|
.addButton(btn => {
|
||||||
|
btn.setButtonText('Ok');
|
||||||
|
btn.setCta();
|
||||||
|
btn.onClick(() => {
|
||||||
|
this.search();
|
||||||
|
});
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.searchBtn = btn;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose(): void {
|
||||||
|
this.closeCallback();
|
||||||
|
const { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,6 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
closeCallback: (err?: Error) => void;
|
closeCallback: (err?: Error) => void;
|
||||||
skipCallback: () => void;
|
skipCallback: () => void;
|
||||||
|
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin, selectModalOptions: SelectModalOptions) {
|
constructor(plugin: MediaDbPlugin, selectModalOptions: SelectModalOptions) {
|
||||||
selectModalOptions = Object.assign({}, SELECT_MODAL_OPTIONS_DEFAULT, selectModalOptions);
|
selectModalOptions = Object.assign({}, SELECT_MODAL_OPTIONS_DEFAULT, selectModalOptions);
|
||||||
super(plugin.app, selectModalOptions.elements, selectModalOptions.multiSelect);
|
super(plugin.app, selectModalOptions.elements, selectModalOptions.multiSelect);
|
||||||
|
|
@ -41,14 +40,14 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renders each suggestion item.
|
// Renders each suggestion item.
|
||||||
renderElement(item: MediaTypeModel, el: HTMLElement) {
|
renderElement(item: MediaTypeModel, el: HTMLElement): void {
|
||||||
el.createEl('div', { text: this.plugin.mediaTypeManager.getFileName(item) });
|
el.createEl('div', { text: this.plugin.mediaTypeManager.getFileName(item) });
|
||||||
el.createEl('small', { text: `${item.getSummary()}\n` });
|
el.createEl('small', { text: `${item.getSummary()}\n` });
|
||||||
el.createEl('small', { text: `${item.type.toUpperCase() + (item.subType ? ` (${item.subType})` : '')} from ${item.dataSource}` });
|
el.createEl('small', { text: `${item.type.toUpperCase() + (item.subType ? ` (${item.subType})` : '')} from ${item.dataSource}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform action on the selected suggestion.
|
// Perform action on the selected suggestion.
|
||||||
submit() {
|
submit(): void {
|
||||||
if (!this.busy) {
|
if (!this.busy) {
|
||||||
this.busy = true;
|
this.busy = true;
|
||||||
this.submitButton.setButtonText('Creating entry...');
|
this.submitButton.setButtonText('Creating entry...');
|
||||||
|
|
@ -56,12 +55,12 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skip() {
|
skip(): void {
|
||||||
this.skipButton.setButtonText('Skipping...');
|
this.skipButton.setButtonText('Skipping...');
|
||||||
this.skipCallback();
|
this.skipCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
elements: T[];
|
elements: T[];
|
||||||
selectModalElements: SelectModalElement<T>[];
|
selectModalElements: SelectModalElement<T>[];
|
||||||
|
|
||||||
|
|
||||||
protected constructor(app: App, elements: T[], allowMultiSelect: boolean = true) {
|
protected constructor(app: App, elements: T[], allowMultiSelect: boolean = true) {
|
||||||
super(app);
|
super(app);
|
||||||
this.allowMultiSelect = allowMultiSelect;
|
this.allowMultiSelect = allowMultiSelect;
|
||||||
|
|
@ -34,18 +33,18 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
this.selectModalElements = [];
|
this.selectModalElements = [];
|
||||||
|
|
||||||
this.scope.register([], 'ArrowUp', (evt) => {
|
this.scope.register([], 'ArrowUp', evt => {
|
||||||
this.highlightUp();
|
this.highlightUp();
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
});
|
});
|
||||||
this.scope.register([], 'ArrowDown', (evt) => {
|
this.scope.register([], 'ArrowDown', evt => {
|
||||||
this.highlightDown();
|
this.highlightDown();
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
});
|
});
|
||||||
this.scope.register([], 'ArrowRight', () => {
|
this.scope.register([], 'ArrowRight', () => {
|
||||||
this.activateHighlighted();
|
this.activateHighlighted();
|
||||||
});
|
});
|
||||||
this.scope.register([], ' ', (evt) => {
|
this.scope.register([], ' ', evt => {
|
||||||
if (this.elementWrapper && this.elementWrapper === document.activeElement) {
|
if (this.elementWrapper && this.elementWrapper === document.activeElement) {
|
||||||
this.activateHighlighted();
|
this.activateHighlighted();
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
@ -60,7 +59,7 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
|
|
||||||
abstract skip(): void;
|
abstract skip(): void;
|
||||||
|
|
||||||
disableAllOtherElements(elementId: number) {
|
disableAllOtherElements(elementId: number): void {
|
||||||
for (const selectModalElement of this.selectModalElements) {
|
for (const selectModalElement of this.selectModalElements) {
|
||||||
if (selectModalElement.id !== elementId) {
|
if (selectModalElement.id !== elementId) {
|
||||||
selectModalElement.setActive(false);
|
selectModalElement.setActive(false);
|
||||||
|
|
@ -68,7 +67,7 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deHighlightAllOtherElements(elementId: number) {
|
deHighlightAllOtherElements(elementId: number): void {
|
||||||
for (const selectModalElement of this.selectModalElements) {
|
for (const selectModalElement of this.selectModalElements) {
|
||||||
if (selectModalElement.id !== elementId) {
|
if (selectModalElement.id !== elementId) {
|
||||||
selectModalElement.setHighlighted(false);
|
selectModalElement.setHighlighted(false);
|
||||||
|
|
@ -76,7 +75,7 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onOpen() {
|
async onOpen(): Promise<void> {
|
||||||
const { contentEl, titleEl } = this;
|
const { contentEl, titleEl } = this;
|
||||||
|
|
||||||
titleEl.createEl('h2', { text: this.title });
|
titleEl.createEl('h2', { text: this.title });
|
||||||
|
|
@ -123,7 +122,7 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
activateHighlighted() {
|
activateHighlighted(): void {
|
||||||
for (const selectModalElement of this.selectModalElements) {
|
for (const selectModalElement of this.selectModalElements) {
|
||||||
if (selectModalElement.isHighlighted()) {
|
if (selectModalElement.isHighlighted()) {
|
||||||
selectModalElement.setActive(!selectModalElement.isActive());
|
selectModalElement.setActive(!selectModalElement.isActive());
|
||||||
|
|
@ -134,7 +133,7 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightUp() {
|
highlightUp(): void {
|
||||||
for (const selectModalElement of this.selectModalElements) {
|
for (const selectModalElement of this.selectModalElements) {
|
||||||
if (selectModalElement.isHighlighted()) {
|
if (selectModalElement.isHighlighted()) {
|
||||||
this.getPreviousSelectModalElement(selectModalElement).setHighlighted(true);
|
this.getPreviousSelectModalElement(selectModalElement).setHighlighted(true);
|
||||||
|
|
@ -146,7 +145,7 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
this.selectModalElements.last().setHighlighted(true);
|
this.selectModalElements.last().setHighlighted(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightDown() {
|
highlightDown(): void {
|
||||||
for (const selectModalElement of this.selectModalElements) {
|
for (const selectModalElement of this.selectModalElements) {
|
||||||
if (selectModalElement.isHighlighted()) {
|
if (selectModalElement.isHighlighted()) {
|
||||||
this.getNextSelectModalElement(selectModalElement).setHighlighted(true);
|
this.getNextSelectModalElement(selectModalElement).setHighlighted(true);
|
||||||
|
|
@ -171,5 +170,4 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
|
|
||||||
return this.selectModalElements.filter(x => x.id === nextId).first();
|
return this.selectModalElements.filter(x => x.id === nextId).first();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export class SelectModalElement<T> {
|
||||||
return this.highlighted;
|
return this.highlighted;
|
||||||
}
|
}
|
||||||
|
|
||||||
setHighlighted(value: boolean) {
|
setHighlighted(value: boolean): void {
|
||||||
this.highlighted = value;
|
this.highlighted = value;
|
||||||
if (this.highlighted) {
|
if (this.highlighted) {
|
||||||
this.addClass(this.hoverClass);
|
this.addClass(this.hoverClass);
|
||||||
|
|
@ -83,5 +83,4 @@ export class SelectModalElement<T> {
|
||||||
this.element.removeClass(cssClass);
|
this.element.removeClass(cssClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@ import {MediaTypeModel} from './MediaTypeModel';
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
|
|
||||||
export class BoardGameModel extends MediaTypeModel {
|
export class BoardGameModel extends MediaTypeModel {
|
||||||
genres: string[];
|
genres: string[];
|
||||||
onlineRating: number;
|
onlineRating: number;
|
||||||
|
complexityRating: number;
|
||||||
|
minPlayers: number;
|
||||||
|
maxPlayers: number;
|
||||||
|
playtime: string;
|
||||||
|
publishers: string[];
|
||||||
image?: string;
|
image?: string;
|
||||||
|
|
||||||
released: boolean;
|
released: boolean;
|
||||||
|
|
@ -15,14 +19,20 @@ export class BoardGameModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: any = {}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.genres = undefined;
|
this.genres = undefined;
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = undefined;
|
||||||
|
this.minPlayers = undefined;
|
||||||
|
this.maxPlayers = undefined;
|
||||||
|
this.playtime = undefined;
|
||||||
|
this.publishers = undefined;
|
||||||
|
this.complexityRating = undefined;
|
||||||
this.image = undefined;
|
this.image = undefined;
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = undefined;
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
played: undefined,
|
played: undefined,
|
||||||
personalRating: undefined,
|
personalRating: undefined,
|
||||||
|
|
@ -48,5 +58,4 @@ export class BoardGameModel extends MediaTypeModel {
|
||||||
getSummary(): string {
|
getSummary(): string {
|
||||||
return this.englishTitle + ' (' + this.year + ')';
|
return this.englishTitle + ' (' + this.year + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import {MediaTypeModel} from './MediaTypeModel';
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
|
|
||||||
export class GameModel extends MediaTypeModel {
|
export class GameModel extends MediaTypeModel {
|
||||||
genres: string[];
|
genres: string[];
|
||||||
onlineRating: number;
|
onlineRating: number;
|
||||||
|
|
@ -16,7 +15,6 @@ export class GameModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: any = {}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|
@ -50,5 +48,4 @@ export class GameModel extends MediaTypeModel {
|
||||||
getSummary(): string {
|
getSummary(): string {
|
||||||
return this.englishTitle + ' (' + this.year + ')';
|
return this.englishTitle + ' (' + this.year + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ export abstract class MediaTypeModel {
|
||||||
|
|
||||||
userData: object;
|
userData: object;
|
||||||
|
|
||||||
|
|
||||||
protected constructor() {
|
protected constructor() {
|
||||||
this.type = undefined;
|
this.type = undefined;
|
||||||
this.subType = undefined;
|
this.subType = undefined;
|
||||||
|
|
@ -41,5 +40,4 @@ export abstract class MediaTypeModel {
|
||||||
delete copy.userData;
|
delete copy.userData;
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,16 @@ import {MediaTypeModel} from './MediaTypeModel';
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
|
|
||||||
export class MovieModel extends MediaTypeModel {
|
export class MovieModel extends MediaTypeModel {
|
||||||
genres: string[];
|
genres: string[];
|
||||||
producer: string;
|
producer: string;
|
||||||
duration: string;
|
duration: string;
|
||||||
onlineRating: number;
|
onlineRating: number;
|
||||||
|
actors: string[];
|
||||||
image: string;
|
image: string;
|
||||||
|
|
||||||
released: boolean;
|
released: boolean;
|
||||||
|
streamingServices: string[];
|
||||||
premiere: string;
|
premiere: string;
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
|
|
@ -26,9 +27,13 @@ export class MovieModel extends MediaTypeModel {
|
||||||
this.producer = undefined;
|
this.producer = undefined;
|
||||||
this.duration = undefined;
|
this.duration = undefined;
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = undefined;
|
||||||
|
this.actors = undefined;
|
||||||
this.image = undefined;
|
this.image = undefined;
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = undefined;
|
||||||
|
this.streamingServices = undefined;
|
||||||
this.premiere = undefined;
|
this.premiere = undefined;
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
watched: undefined,
|
watched: undefined,
|
||||||
lastWatched: undefined,
|
lastWatched: undefined,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import {MediaTypeModel} from './MediaTypeModel';
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
|
|
||||||
export class MusicReleaseModel extends MediaTypeModel {
|
export class MusicReleaseModel extends MediaTypeModel {
|
||||||
type: string;
|
type: string;
|
||||||
subType: string;
|
subType: string;
|
||||||
|
|
@ -50,8 +49,7 @@ export class MusicReleaseModel extends MediaTypeModel {
|
||||||
|
|
||||||
getSummary(): string {
|
getSummary(): string {
|
||||||
let summary = this.title + ' (' + this.year + ')';
|
let summary = this.title + ' (' + this.year + ')';
|
||||||
if (this.artists.length > 0)
|
if (this.artists.length > 0) summary += ' - ' + this.artists.join(', ');
|
||||||
summary += ' - ' + this.artists.join(', ');
|
|
||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import {MediaTypeModel} from './MediaTypeModel';
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
|
|
||||||
export class SeriesModel extends MediaTypeModel {
|
export class SeriesModel extends MediaTypeModel {
|
||||||
type: string;
|
type: string;
|
||||||
subType: string;
|
subType: string;
|
||||||
|
|
@ -18,9 +17,11 @@ export class SeriesModel extends MediaTypeModel {
|
||||||
episodes: number;
|
episodes: number;
|
||||||
duration: string;
|
duration: string;
|
||||||
onlineRating: number;
|
onlineRating: number;
|
||||||
|
actors: string[];
|
||||||
image: string;
|
image: string;
|
||||||
|
|
||||||
released: boolean;
|
released: boolean;
|
||||||
|
streamingServices: string[];
|
||||||
airing: boolean;
|
airing: boolean;
|
||||||
airedFrom: string;
|
airedFrom: string;
|
||||||
airedTo: string;
|
airedTo: string;
|
||||||
|
|
@ -39,11 +40,15 @@ export class SeriesModel extends MediaTypeModel {
|
||||||
this.episodes = undefined;
|
this.episodes = undefined;
|
||||||
this.duration = undefined;
|
this.duration = undefined;
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = undefined;
|
||||||
|
this.actors = undefined;
|
||||||
this.image = undefined;
|
this.image = undefined;
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = undefined;
|
||||||
|
this.streamingServices = undefined;
|
||||||
this.airing = undefined;
|
this.airing = undefined;
|
||||||
this.airedFrom = undefined;
|
this.airedFrom = undefined;
|
||||||
this.airedTo = undefined;
|
this.airedTo = undefined;
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
watched: undefined,
|
watched: undefined,
|
||||||
lastWatched: undefined,
|
lastWatched: undefined,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import {MediaTypeModel} from './MediaTypeModel';
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
|
|
||||||
export class WikiModel extends MediaTypeModel {
|
export class WikiModel extends MediaTypeModel {
|
||||||
type: string;
|
type: string;
|
||||||
subType: string;
|
subType: string;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export class PropertyMapper {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
newObj[propertyMapping.newProperty] = value;
|
newObj[propertyMapping.newProperty] = value;
|
||||||
} else if (propertyMapping.mapping === PropertyMappingOption.Remove) {
|
} else if (propertyMapping.mapping === PropertyMappingOption.Remove) {
|
||||||
|
// do nothing
|
||||||
} else if (propertyMapping.mapping === PropertyMappingOption.Default) {
|
} else if (propertyMapping.mapping === PropertyMappingOption.Default) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
newObj[key] = value;
|
newObj[key] = value;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export class PropertyMappingModel {
|
||||||
this.properties = properties ?? [];
|
this.properties = properties ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(): { res: boolean, err?: Error } {
|
validate(): { res: boolean; err?: Error } {
|
||||||
console.debug(`MDB | validated property mappings for ${this.type}`);
|
console.debug(`MDB | validated property mappings for ${this.type}`);
|
||||||
|
|
||||||
// check properties
|
// check properties
|
||||||
|
|
@ -43,7 +43,9 @@ export class PropertyMappingModel {
|
||||||
// two or more properties are mapped to the same property
|
// two or more properties are mapped to the same property
|
||||||
return {
|
return {
|
||||||
res: false,
|
res: false,
|
||||||
err: new PropertyMappingNameConflictError(`Multiple remapped properties (${propertiesWithSameTarget.map(x => x.toString()).toString()}) may not share the same name.`),
|
err: new PropertyMappingNameConflictError(
|
||||||
|
`Multiple remapped properties (${propertiesWithSameTarget.map(x => x.toString()).toString()}) may not share the same name.`
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +95,7 @@ export class PropertyMapping {
|
||||||
this.locked = locked ?? false;
|
this.locked = locked ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(): { res: boolean, err?: Error } {
|
validate(): { res: boolean; err?: Error } {
|
||||||
// locked property may only be default
|
// locked property may only be default
|
||||||
if (this.locked) {
|
if (this.locked) {
|
||||||
if (this.mapping === PropertyMappingOption.Remove) {
|
if (this.mapping === PropertyMappingOption.Remove) {
|
||||||
|
|
|
||||||
|
|
@ -8,42 +8,44 @@ import {PropertyMapping, PropertyMappingModel, PropertyMappingOption} from './Pr
|
||||||
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
|
||||||
|
|
||||||
export interface MediaDbPluginSettings {
|
export interface MediaDbPluginSettings {
|
||||||
folder: string,
|
OMDbKey: string;
|
||||||
OMDbKey: string,
|
sfwFilter: boolean;
|
||||||
sfwFilter: boolean,
|
|
||||||
useCustomYamlStringifier: boolean;
|
useCustomYamlStringifier: boolean;
|
||||||
templates: boolean,
|
templates: boolean;
|
||||||
|
|
||||||
|
movieTemplate: string;
|
||||||
|
seriesTemplate: string;
|
||||||
|
gameTemplate: string;
|
||||||
|
wikiTemplate: string;
|
||||||
|
musicReleaseTemplate: string;
|
||||||
|
boardgameTemplate: string;
|
||||||
|
|
||||||
movieTemplate: string,
|
movieFileNameTemplate: string;
|
||||||
seriesTemplate: string,
|
seriesFileNameTemplate: string;
|
||||||
gameTemplate: string,
|
gameFileNameTemplate: string;
|
||||||
wikiTemplate: string,
|
wikiFileNameTemplate: string;
|
||||||
musicReleaseTemplate: string,
|
musicReleaseFileNameTemplate: string;
|
||||||
boardgameTemplate: string,
|
boardgameFileNameTemplate: string;
|
||||||
|
|
||||||
movieFileNameTemplate: string,
|
moviePropertyConversionRules: string;
|
||||||
seriesFileNameTemplate: string,
|
seriesPropertyConversionRules: string;
|
||||||
gameFileNameTemplate: string,
|
gamePropertyConversionRules: string;
|
||||||
wikiFileNameTemplate: string,
|
wikiPropertyConversionRules: string;
|
||||||
musicReleaseFileNameTemplate: string,
|
musicReleasePropertyConversionRules: string;
|
||||||
boardgameFileNameTemplate: string,
|
boardgamePropertyConversionRules: string;
|
||||||
|
|
||||||
moviePropertyConversionRules: string,
|
movieFolder: string;
|
||||||
seriesPropertyConversionRules: string,
|
seriesFolder: string;
|
||||||
gamePropertyConversionRules: string,
|
gameFolder: string;
|
||||||
wikiPropertyConversionRules: string,
|
wikiFolder: string;
|
||||||
musicReleasePropertyConversionRules: string,
|
musicReleaseFolder: string;
|
||||||
boardgamePropertyConversionRules: string,
|
boardgameFolder: string;
|
||||||
|
|
||||||
propertyMappingModels: PropertyMappingModel[],
|
|
||||||
|
|
||||||
|
propertyMappingModels: PropertyMappingModel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
||||||
folder: 'Media DB',
|
|
||||||
OMDbKey: '',
|
OMDbKey: '',
|
||||||
sfwFilter: true,
|
sfwFilter: true,
|
||||||
useCustomYamlStringifier: true,
|
useCustomYamlStringifier: true,
|
||||||
|
|
@ -70,13 +72,20 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
||||||
musicReleasePropertyConversionRules: '',
|
musicReleasePropertyConversionRules: '',
|
||||||
boardgamePropertyConversionRules: '',
|
boardgamePropertyConversionRules: '',
|
||||||
|
|
||||||
|
movieFolder: 'Media DB/movies',
|
||||||
|
seriesFolder: 'Media DB/series',
|
||||||
|
gameFolder: 'Media DB/games',
|
||||||
|
wikiFolder: 'Media DB/wiki',
|
||||||
|
musicReleaseFolder: 'Media DB/music',
|
||||||
|
boardgameFolder: 'Media DB/boardgames',
|
||||||
|
|
||||||
propertyMappingModels: [],
|
propertyMappingModels: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const lockedPropertyMappings: string[] = ['type', 'id', 'dataSource'];
|
export const lockedPropertyMappings: string[] = ['type', 'id', 'dataSource'];
|
||||||
|
|
||||||
export function getDefaultSettings(plugin: MediaDbPlugin): MediaDbPluginSettings {
|
export function getDefaultSettings(plugin: MediaDbPlugin): MediaDbPluginSettings {
|
||||||
let defaultSettings = DEFAULT_SETTINGS;
|
const defaultSettings = DEFAULT_SETTINGS;
|
||||||
|
|
||||||
// construct property mapping defaults
|
// construct property mapping defaults
|
||||||
const propertyMappingModels: PropertyMappingModel[] = [];
|
const propertyMappingModels: PropertyMappingModel[] = [];
|
||||||
|
|
@ -89,9 +98,7 @@ export function getDefaultSettings(plugin: MediaDbPlugin): MediaDbPluginSettings
|
||||||
const propertyMappingModel: PropertyMappingModel = new PropertyMappingModel(mediaType);
|
const propertyMappingModel: PropertyMappingModel = new PropertyMappingModel(mediaType);
|
||||||
|
|
||||||
for (const key of Object.keys(metadataObj)) {
|
for (const key of Object.keys(metadataObj)) {
|
||||||
propertyMappingModel.properties.push(
|
propertyMappingModel.properties.push(new PropertyMapping(key, '', PropertyMappingOption.Default, lockedPropertyMappings.contains(key)));
|
||||||
new PropertyMapping(key, '', PropertyMappingOption.Default, lockedPropertyMappings.contains(key)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyMappingModels.push(propertyMappingModel);
|
propertyMappingModels.push(propertyMappingModel);
|
||||||
|
|
@ -116,19 +123,6 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
|
|
||||||
containerEl.createEl('h2', { text: 'Media DB Plugin Settings' });
|
containerEl.createEl('h2', { text: 'Media DB Plugin Settings' });
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('New file location')
|
|
||||||
.setDesc('New media db entries will be placed here.')
|
|
||||||
.addSearch(cb => {
|
|
||||||
new FolderSuggest(this.app, cb.inputEl);
|
|
||||||
cb.setPlaceholder('Example: folder1/folder2')
|
|
||||||
.setValue(this.plugin.settings.folder)
|
|
||||||
.onChange(data => {
|
|
||||||
this.plugin.settings.folder = data;
|
|
||||||
this.plugin.saveSettings();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName('OMDb API key')
|
.setName('OMDb API key')
|
||||||
.setDesc('API key for "www.omdbapi.com".')
|
.setDesc('API key for "www.omdbapi.com".')
|
||||||
|
|
@ -145,8 +139,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
.setName('SFW filter')
|
.setName('SFW filter')
|
||||||
.setDesc('Only shows SFW results for APIs that offer filtering.')
|
.setDesc('Only shows SFW results for APIs that offer filtering.')
|
||||||
.addToggle(cb => {
|
.addToggle(cb => {
|
||||||
cb.setValue(this.plugin.settings.sfwFilter)
|
cb.setValue(this.plugin.settings.sfwFilter).onChange(data => {
|
||||||
.onChange(data => {
|
|
||||||
this.plugin.settings.sfwFilter = data;
|
this.plugin.settings.sfwFilter = data;
|
||||||
this.plugin.saveSettings();
|
this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
|
|
@ -156,8 +149,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
.setName('YAML formatter')
|
.setName('YAML formatter')
|
||||||
.setDesc('Add optional quotation marks around strings in the metadata block.')
|
.setDesc('Add optional quotation marks around strings in the metadata block.')
|
||||||
.addToggle(cb => {
|
.addToggle(cb => {
|
||||||
cb.setValue(this.plugin.settings.useCustomYamlStringifier)
|
cb.setValue(this.plugin.settings.useCustomYamlStringifier).onChange(data => {
|
||||||
.onChange(data => {
|
|
||||||
this.plugin.settings.useCustomYamlStringifier = data;
|
this.plugin.settings.useCustomYamlStringifier = data;
|
||||||
this.plugin.saveSettings();
|
this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
|
|
@ -167,13 +159,92 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
.setName('Resolve {{ tags }} in templates')
|
.setName('Resolve {{ tags }} in templates')
|
||||||
.setDesc('Whether to resolve {{ tags }} in templates. The spaces inside the curly braces are important.')
|
.setDesc('Whether to resolve {{ tags }} in templates. The spaces inside the curly braces are important.')
|
||||||
.addToggle(cb => {
|
.addToggle(cb => {
|
||||||
cb.setValue(this.plugin.settings.templates)
|
cb.setValue(this.plugin.settings.templates).onChange(data => {
|
||||||
.onChange(data => {
|
|
||||||
this.plugin.settings.templates = data;
|
this.plugin.settings.templates = data;
|
||||||
this.plugin.saveSettings();
|
this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
containerEl.createEl('h3', { text: 'New File Location' });
|
||||||
|
// region new file location
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Movie Folder')
|
||||||
|
.setDesc('Where newly imported movies should be places.')
|
||||||
|
.addSearch(cb => {
|
||||||
|
new FolderSuggest(this.app, cb.inputEl);
|
||||||
|
cb.setPlaceholder(DEFAULT_SETTINGS.movieFolder)
|
||||||
|
.setValue(this.plugin.settings.movieFolder)
|
||||||
|
.onChange(data => {
|
||||||
|
this.plugin.settings.movieFolder = data;
|
||||||
|
this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Series Folder')
|
||||||
|
.setDesc('Where newly imported series should be places.')
|
||||||
|
.addSearch(cb => {
|
||||||
|
new FolderSuggest(this.app, cb.inputEl);
|
||||||
|
cb.setPlaceholder(DEFAULT_SETTINGS.seriesFolder)
|
||||||
|
.setValue(this.plugin.settings.seriesFolder)
|
||||||
|
.onChange(data => {
|
||||||
|
this.plugin.settings.seriesFolder = data;
|
||||||
|
this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Game Folder')
|
||||||
|
.setDesc('Where newly imported games should be places.')
|
||||||
|
.addSearch(cb => {
|
||||||
|
new FolderSuggest(this.app, cb.inputEl);
|
||||||
|
cb.setPlaceholder(DEFAULT_SETTINGS.gameFolder)
|
||||||
|
.setValue(this.plugin.settings.gameFolder)
|
||||||
|
.onChange(data => {
|
||||||
|
this.plugin.settings.gameFolder = data;
|
||||||
|
this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Wiki Folder')
|
||||||
|
.setDesc('Where newly imported wiki articles should be places.')
|
||||||
|
.addSearch(cb => {
|
||||||
|
new FolderSuggest(this.app, cb.inputEl);
|
||||||
|
cb.setPlaceholder(DEFAULT_SETTINGS.wikiFolder)
|
||||||
|
.setValue(this.plugin.settings.wikiFolder)
|
||||||
|
.onChange(data => {
|
||||||
|
this.plugin.settings.wikiFolder = data;
|
||||||
|
this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Music Folder')
|
||||||
|
.setDesc('Where newly imported music should be places.')
|
||||||
|
.addSearch(cb => {
|
||||||
|
new FolderSuggest(this.app, cb.inputEl);
|
||||||
|
cb.setPlaceholder(DEFAULT_SETTINGS.musicReleaseFolder)
|
||||||
|
.setValue(this.plugin.settings.musicReleaseFolder)
|
||||||
|
.onChange(data => {
|
||||||
|
this.plugin.settings.musicReleaseFolder = data;
|
||||||
|
this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Board Game Folder')
|
||||||
|
.setDesc('Where newly imported board games should be places.')
|
||||||
|
.addSearch(cb => {
|
||||||
|
new FileSuggest(this.app, cb.inputEl);
|
||||||
|
cb.setPlaceholder(DEFAULT_SETTINGS.boardgameFolder)
|
||||||
|
.setValue(this.plugin.settings.boardgameFolder)
|
||||||
|
.onChange(data => {
|
||||||
|
this.plugin.settings.boardgameFolder = data;
|
||||||
|
this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// endregion
|
||||||
|
|
||||||
containerEl.createEl('h3', { text: 'Template Settings' });
|
containerEl.createEl('h3', { text: 'Template Settings' });
|
||||||
// region templates
|
// region templates
|
||||||
|
|
@ -335,7 +406,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
|
|
||||||
containerEl.createEl('h3', { text: 'Property Mappings' });
|
containerEl.createEl('h3', { text: 'Property Mappings' });
|
||||||
|
|
||||||
let propertyMappingExplanation = containerEl.createEl('div');
|
const propertyMappingExplanation = containerEl.createEl('div');
|
||||||
propertyMappingExplanation.innerHTML = `
|
propertyMappingExplanation.innerHTML = `
|
||||||
<p>Allow you to remap the metadata fields of newly created media db entries.</p>
|
<p>Allow you to remap the metadata fields of newly created media db entries.</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -350,13 +421,12 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
Don't forget to save your changes using the save button for each individual category.
|
Don't forget to save your changes using the save button for each individual category.
|
||||||
</p>`;
|
</p>`;
|
||||||
|
|
||||||
|
|
||||||
new PropertyMappingModelsComponent({
|
new PropertyMappingModelsComponent({
|
||||||
target: this.containerEl,
|
target: this.containerEl,
|
||||||
props: {
|
props: {
|
||||||
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
|
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
|
||||||
save: (model: PropertyMappingModel) => {
|
save: (model: PropertyMappingModel): void => {
|
||||||
let propertyMappingModels: PropertyMappingModel[] = [];
|
const propertyMappingModels: PropertyMappingModel[] = [];
|
||||||
|
|
||||||
for (const model2 of this.plugin.settings.propertyMappingModels) {
|
for (const model2 of this.plugin.settings.propertyMappingModels) {
|
||||||
if (model2.type === model.type) {
|
if (model2.type === model.type) {
|
||||||
|
|
@ -374,7 +444,5 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
});
|
});
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,7 @@ export class FolderSuggest extends TextInputSuggest<TFolder> {
|
||||||
const lowerCaseInputStr = inputStr.toLowerCase();
|
const lowerCaseInputStr = inputStr.toLowerCase();
|
||||||
|
|
||||||
abstractFiles.forEach((folder: TAbstractFile) => {
|
abstractFiles.forEach((folder: TAbstractFile) => {
|
||||||
if (
|
if (folder instanceof TFolder && folder.path.toLowerCase().contains(lowerCaseInputStr)) {
|
||||||
folder instanceof TFolder &&
|
|
||||||
folder.path.toLowerCase().contains(lowerCaseInputStr)
|
|
||||||
) {
|
|
||||||
folders.push(folder);
|
folders.push(folder);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,27 +16,23 @@ export class Suggest<T> {
|
||||||
this.containerEl = containerEl;
|
this.containerEl = containerEl;
|
||||||
|
|
||||||
containerEl.on('click', '.suggestion-item', this.onSuggestionClick.bind(this));
|
containerEl.on('click', '.suggestion-item', this.onSuggestionClick.bind(this));
|
||||||
containerEl.on(
|
containerEl.on('mousemove', '.suggestion-item', this.onSuggestionMouseover.bind(this));
|
||||||
'mousemove',
|
|
||||||
'.suggestion-item',
|
|
||||||
this.onSuggestionMouseover.bind(this),
|
|
||||||
);
|
|
||||||
|
|
||||||
scope.register([], 'ArrowUp', (event) => {
|
scope.register([], 'ArrowUp', event => {
|
||||||
if (!event.isComposing) {
|
if (!event.isComposing) {
|
||||||
this.setSelectedItem(this.selectedItem - 1, true);
|
this.setSelectedItem(this.selectedItem - 1, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.register([], 'ArrowDown', (event) => {
|
scope.register([], 'ArrowDown', event => {
|
||||||
if (!event.isComposing) {
|
if (!event.isComposing) {
|
||||||
this.setSelectedItem(this.selectedItem + 1, true);
|
this.setSelectedItem(this.selectedItem + 1, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.register([], 'Enter', (event) => {
|
scope.register([], 'Enter', event => {
|
||||||
if (!event.isComposing) {
|
if (!event.isComposing) {
|
||||||
this.useSelectedItem(event);
|
this.useSelectedItem(event);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -57,11 +53,11 @@ export class Suggest<T> {
|
||||||
this.setSelectedItem(item, false);
|
this.setSelectedItem(item, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSuggestions(values: T[]) {
|
setSuggestions(values: T[]): void {
|
||||||
this.containerEl.empty();
|
this.containerEl.empty();
|
||||||
const suggestionEls: HTMLDivElement[] = [];
|
const suggestionEls: HTMLDivElement[] = [];
|
||||||
|
|
||||||
values.forEach((value) => {
|
values.forEach(value => {
|
||||||
const suggestionEl = this.containerEl.createDiv('suggestion-item');
|
const suggestionEl = this.containerEl.createDiv('suggestion-item');
|
||||||
this.owner.renderSuggestion(value, suggestionEl);
|
this.owner.renderSuggestion(value, suggestionEl);
|
||||||
suggestionEls.push(suggestionEl);
|
suggestionEls.push(suggestionEl);
|
||||||
|
|
@ -72,14 +68,14 @@ export class Suggest<T> {
|
||||||
this.setSelectedItem(0, false);
|
this.setSelectedItem(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
useSelectedItem(event: MouseEvent | KeyboardEvent) {
|
useSelectedItem(event: MouseEvent | KeyboardEvent): void {
|
||||||
const currentValue = this.values[this.selectedItem];
|
const currentValue = this.values[this.selectedItem];
|
||||||
if (currentValue) {
|
if (currentValue) {
|
||||||
this.owner.selectSuggestion(currentValue, event);
|
this.owner.selectSuggestion(currentValue, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedItem(selectedIndex: number, scrollIntoView: boolean) {
|
setSelectedItem(selectedIndex: number, scrollIntoView: boolean): void {
|
||||||
const normalizedIndex = this.suggestions.length > 0 ? wrapAround(selectedIndex, this.suggestions.length) : 0;
|
const normalizedIndex = this.suggestions.length > 0 ? wrapAround(selectedIndex, this.suggestions.length) : 0;
|
||||||
const prevSelectedSuggestion = this.suggestions[this.selectedItem];
|
const prevSelectedSuggestion = this.suggestions[this.selectedItem];
|
||||||
const selectedSuggestion = this.suggestions[normalizedIndex];
|
const selectedSuggestion = this.suggestions[normalizedIndex];
|
||||||
|
|
@ -145,7 +141,7 @@ export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
|
||||||
{
|
{
|
||||||
name: 'sameWidth',
|
name: 'sameWidth',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
fn: ({state, instance}) => {
|
fn: ({ state, instance }): void => {
|
||||||
// Note: positioning needs to be calculated twice -
|
// Note: positioning needs to be calculated twice -
|
||||||
// first pass - positioning it according to the width of the popper
|
// first pass - positioning it according to the width of the popper
|
||||||
// second pass - position it with the width bound to the reference element
|
// second pass - position it with the width bound to the reference element
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,9 @@ import WikipediaMockResponse from './ResponseMocks/WikipediaMockResponse.json';
|
||||||
enableFetchMocks();
|
enableFetchMocks();
|
||||||
export let apiMock: OMDbAPI | MALAPI | LocGovAPI | MusicBrainzAPI | SteamAPI | WikipediaAPI;
|
export let apiMock: OMDbAPI | MALAPI | LocGovAPI | MusicBrainzAPI | SteamAPI | WikipediaAPI;
|
||||||
|
|
||||||
describe.each(
|
describe.each([{ name: OMDbAPI }, { name: MALAPI }, { name: LocGovAPI }, { name: MusicBrainzAPI }, { name: SteamAPI }, { name: WikipediaAPI }])(
|
||||||
[
|
'$name.name',
|
||||||
{name: OMDbAPI},
|
({ name: parameterizedApi }) => {
|
||||||
{name: MALAPI},
|
|
||||||
{name: LocGovAPI},
|
|
||||||
{name: MusicBrainzAPI},
|
|
||||||
{name: SteamAPI},
|
|
||||||
{name: WikipediaAPI},
|
|
||||||
],
|
|
||||||
)('$name.name', ({name: parameterizedApi}) => {
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
let settingsMock: MediaDbPluginSettings = {} as MediaDbPluginSettings;
|
let settingsMock: MediaDbPluginSettings = {} as MediaDbPluginSettings;
|
||||||
let pluginMock = {} as MediaDbPlugin;
|
let pluginMock = {} as MediaDbPlugin;
|
||||||
|
|
@ -107,4 +100,5 @@ describe.each(
|
||||||
await expect(apiMock.searchByTitle('Hooking Season Playtest')).resolves.toEqual(ret);
|
await expect(apiMock.searchByTitle('Hooking Season Playtest')).resolves.toEqual(ret);
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import MediaDbPlugin from '../main';
|
||||||
export class TestAPI extends APIModel {
|
export class TestAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin) {
|
constructor(plugin: MediaDbPlugin) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|
@ -16,7 +15,6 @@ export class TestAPI extends APIModel {
|
||||||
this.types = [];
|
this.types = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async getById(id: string): Promise<MediaTypeModel> {
|
async getById(id: string): Promise<MediaTypeModel> {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,73 +13,83 @@ import WikipediaMockResponse from './ResponseMocks/WikipediaMockResponse.json';
|
||||||
export function setWikipediaResponseMock() {
|
export function setWikipediaResponseMock() {
|
||||||
let ret = [];
|
let ret = [];
|
||||||
let wikiresponse = WikipediaMockResponse.query.search[0];
|
let wikiresponse = WikipediaMockResponse.query.search[0];
|
||||||
ret.push(new WikiModel({
|
ret.push(
|
||||||
|
new WikiModel({
|
||||||
type: 'wiki',
|
type: 'wiki',
|
||||||
title: wikiresponse.title,
|
title: wikiresponse.title,
|
||||||
englishTitle: wikiresponse.title,
|
englishTitle: wikiresponse.title,
|
||||||
year: '',
|
year: '',
|
||||||
dataSource: apiMock.apiName,
|
dataSource: apiMock.apiName,
|
||||||
id: wikiresponse.pageid,
|
id: wikiresponse.pageid,
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setOMDbResponseMock() {
|
export function setOMDbResponseMock() {
|
||||||
let ret = [];
|
let ret = [];
|
||||||
let omdbresponse = OMDBMockMovie.Search[0];
|
let omdbresponse = OMDBMockMovie.Search[0];
|
||||||
ret.push(new MovieModel({
|
ret.push(
|
||||||
|
new MovieModel({
|
||||||
type: 'wiki',
|
type: 'wiki',
|
||||||
title: omdbresponse.Title,
|
title: omdbresponse.Title,
|
||||||
englishTitle: omdbresponse.Title,
|
englishTitle: omdbresponse.Title,
|
||||||
year: omdbresponse.Year,
|
year: omdbresponse.Year,
|
||||||
dataSource: apiMock.apiName,
|
dataSource: apiMock.apiName,
|
||||||
id: omdbresponse.imdbID,
|
id: omdbresponse.imdbID,
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setMALResponseMock() {
|
export function setMALResponseMock() {
|
||||||
let ret = [];
|
let ret = [];
|
||||||
let result = MALMockMovie.data[0];
|
let result = MALMockMovie.data[0];
|
||||||
ret.push(new MovieModel({
|
ret.push(
|
||||||
|
new MovieModel({
|
||||||
type: result.type,
|
type: result.type,
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title_english,
|
englishTitle: result.title_english,
|
||||||
year: result.aired.prop.from.year,
|
year: result.aired.prop.from.year,
|
||||||
dataSource: apiMock.apiName,
|
dataSource: apiMock.apiName,
|
||||||
id: result.mal_id,
|
id: result.mal_id,
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setSteamResponseMock() {
|
export function setSteamResponseMock() {
|
||||||
let ret = [];
|
let ret = [];
|
||||||
let steamResponse = SteamAPIResponseMock.applist.apps[0];
|
let steamResponse = SteamAPIResponseMock.applist.apps[0];
|
||||||
ret.push(new GameModel({
|
ret.push(
|
||||||
|
new GameModel({
|
||||||
type: MediaType.Game,
|
type: MediaType.Game,
|
||||||
title: steamResponse.name,
|
title: steamResponse.name,
|
||||||
englishTitle: steamResponse.name,
|
englishTitle: steamResponse.name,
|
||||||
year: '',
|
year: '',
|
||||||
dataSource: apiMock.apiName,
|
dataSource: apiMock.apiName,
|
||||||
id: steamResponse.appid,
|
id: steamResponse.appid,
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setMusicBrainzResponseMock() {
|
export function setMusicBrainzResponseMock() {
|
||||||
let ret = [];
|
let ret = [];
|
||||||
let result = MusicBrainzResponseMock['release-groups'][0];
|
let result = MusicBrainzResponseMock['release-groups'][0];
|
||||||
ret.push(new MusicReleaseModel({
|
ret.push(
|
||||||
|
new MusicReleaseModel({
|
||||||
type: 'musicRelease',
|
type: 'musicRelease',
|
||||||
title: result.title,
|
title: result.title,
|
||||||
englishTitle: result.title,
|
englishTitle: result.title,
|
||||||
year: (new Date(result['first-release-date'])).getFullYear().toString(),
|
year: new Date(result['first-release-date']).getFullYear().toString(),
|
||||||
dataSource: apiMock.apiName,
|
dataSource: apiMock.apiName,
|
||||||
url: '',
|
url: '',
|
||||||
id: result.id,
|
id: result.id,
|
||||||
|
|
||||||
artists: result['artist-credit'].map((a: any) => a.name),
|
artists: result['artist-credit'].map((a: any) => a.name),
|
||||||
subType: result['primary-type'],
|
subType: result['primary-type'],
|
||||||
} as MusicReleaseModel));
|
} as MusicReleaseModel)
|
||||||
|
);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1185
src/utils/IconList.ts
Normal file
1185
src/utils/IconList.ts
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@ import {MediaDbPluginSettings} from '../settings/Settings';
|
||||||
import { MediaType } from './MediaType';
|
import { MediaType } from './MediaType';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
import { replaceTags } from './Utils';
|
import { replaceTags } from './Utils';
|
||||||
import {App, TFile} from 'obsidian';
|
import { App, TAbstractFile, TFile, TFolder } from 'obsidian';
|
||||||
import { MovieModel } from '../models/MovieModel';
|
import { MovieModel } from '../models/MovieModel';
|
||||||
import { SeriesModel } from '../models/SeriesModel';
|
import { SeriesModel } from '../models/SeriesModel';
|
||||||
import { GameModel } from '../models/GameModel';
|
import { GameModel } from '../models/GameModel';
|
||||||
|
|
@ -15,11 +15,11 @@ export const MEDIA_TYPES: MediaType[] = [MediaType.Movie, MediaType.Series, Medi
|
||||||
export class MediaTypeManager {
|
export class MediaTypeManager {
|
||||||
mediaFileNameTemplateMap: Map<MediaType, string>;
|
mediaFileNameTemplateMap: Map<MediaType, string>;
|
||||||
mediaTemplateMap: Map<MediaType, string>;
|
mediaTemplateMap: Map<MediaType, string>;
|
||||||
|
mediaFolderMap: Map<MediaType, string>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {}
|
||||||
}
|
|
||||||
|
|
||||||
updateTemplates(settings: MediaDbPluginSettings) {
|
updateTemplates(settings: MediaDbPluginSettings): void {
|
||||||
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
|
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
|
||||||
this.mediaFileNameTemplateMap.set(MediaType.Movie, settings.movieFileNameTemplate);
|
this.mediaFileNameTemplateMap.set(MediaType.Movie, settings.movieFileNameTemplate);
|
||||||
this.mediaFileNameTemplateMap.set(MediaType.Series, settings.seriesFileNameTemplate);
|
this.mediaFileNameTemplateMap.set(MediaType.Series, settings.seriesFileNameTemplate);
|
||||||
|
|
@ -37,18 +37,31 @@ export class MediaTypeManager {
|
||||||
this.mediaTemplateMap.set(MediaType.BoardGame, settings.boardgameTemplate);
|
this.mediaTemplateMap.set(MediaType.BoardGame, settings.boardgameTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFolders(settings: MediaDbPluginSettings): void {
|
||||||
|
this.mediaFolderMap = new Map<MediaType, string>();
|
||||||
|
this.mediaFolderMap.set(MediaType.Movie, settings.movieFolder);
|
||||||
|
this.mediaFolderMap.set(MediaType.Series, settings.seriesFolder);
|
||||||
|
this.mediaFolderMap.set(MediaType.Game, settings.gameFolder);
|
||||||
|
this.mediaFolderMap.set(MediaType.Wiki, settings.wikiFolder);
|
||||||
|
this.mediaFolderMap.set(MediaType.MusicRelease, settings.musicReleaseFolder);
|
||||||
|
this.mediaFolderMap.set(MediaType.BoardGame, settings.boardgameFolder);
|
||||||
|
}
|
||||||
|
|
||||||
getFileName(mediaTypeModel: MediaTypeModel): string {
|
getFileName(mediaTypeModel: MediaTypeModel): string {
|
||||||
return replaceTags(this.mediaFileNameTemplateMap.get(mediaTypeModel.getMediaType()), mediaTypeModel);
|
return replaceTags(this.mediaFileNameTemplateMap.get(mediaTypeModel.getMediaType()), mediaTypeModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTemplate(mediaTypeModel: MediaTypeModel, app: App) {
|
async getTemplate(mediaTypeModel: MediaTypeModel, app: App): Promise<string> {
|
||||||
const templateFileName = this.mediaTemplateMap.get(mediaTypeModel.getMediaType());
|
const templateFileName = this.mediaTemplateMap.get(mediaTypeModel.getMediaType());
|
||||||
|
|
||||||
if (!templateFileName) {
|
if (!templateFileName) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const templateFile: TFile = app.vault.getFiles().filter((f: TFile) => f.name === templateFileName).first();
|
const templateFile: TFile = app.vault
|
||||||
|
.getFiles()
|
||||||
|
.filter((f: TFile) => f.name === templateFileName)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!templateFile) {
|
if (!templateFile) {
|
||||||
return '';
|
return '';
|
||||||
|
|
@ -59,6 +72,26 @@ export class MediaTypeManager {
|
||||||
return replaceTags(template, mediaTypeModel);
|
return replaceTags(template, mediaTypeModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFolder(mediaTypeModel: MediaTypeModel, app: App): Promise<TFolder> {
|
||||||
|
let folderPath = this.mediaFolderMap.get(mediaTypeModel.getMediaType());
|
||||||
|
|
||||||
|
if (!folderPath) {
|
||||||
|
folderPath = `/`;
|
||||||
|
}
|
||||||
|
console.log(folderPath);
|
||||||
|
|
||||||
|
if (!(await app.vault.adapter.exists(folderPath))) {
|
||||||
|
await app.vault.createFolder(folderPath);
|
||||||
|
}
|
||||||
|
const folder: TAbstractFile = app.vault.getAbstractFileByPath(folderPath);
|
||||||
|
|
||||||
|
if (!(folder instanceof TFolder)) {
|
||||||
|
throw Error(`Expected ${folder} to be instance of TFolder`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes an object and a MediaType and turns the object into an instance of a MediaTypeModel corresponding to the MediaType passed in.
|
* Takes an object and a MediaType and turns the object into an instance of a MediaTypeModel corresponding to the MediaType passed in.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import {Notice} from 'obsidian';
|
||||||
import MediaDbPlugin from '../main';
|
import MediaDbPlugin from '../main';
|
||||||
import { MediaDbPreviewModal } from 'src/modals/MediaDbPreviewModal';
|
import { MediaDbPreviewModal } from 'src/modals/MediaDbPreviewModal';
|
||||||
import { CreateNoteOptions } from './Utils';
|
import { CreateNoteOptions } from './Utils';
|
||||||
|
import { MediaDbSearchModal } from '../modals/MediaDbSearchModal';
|
||||||
|
import { MediaType } from './MediaType';
|
||||||
|
|
||||||
export enum ModalResultCode {
|
export enum ModalResultCode {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
|
|
@ -15,15 +16,26 @@ export enum ModalResultCode {
|
||||||
ERROR,
|
ERROR,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object containing the data {@link ModalHelper.createSearchModal} returns.
|
||||||
|
* On {@link ModalResultCode.SUCCESS} this contains {@link SearchModalData}.
|
||||||
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
|
*/
|
||||||
|
export interface SearchModalResult {
|
||||||
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
||||||
|
data?: SearchModalData;
|
||||||
|
error?: Error;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing the data {@link ModalHelper.createAdvancedSearchModal} returns.
|
* Object containing the data {@link ModalHelper.createAdvancedSearchModal} returns.
|
||||||
* On {@link ModalResultCode.SUCCESS} this contains {@link AdvancedSearchModalData}.
|
* On {@link ModalResultCode.SUCCESS} this contains {@link AdvancedSearchModalData}.
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface AdvancedSearchModalResult {
|
export interface AdvancedSearchModalResult {
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR,
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
||||||
data?: AdvancedSearchModalData,
|
data?: AdvancedSearchModalData;
|
||||||
error?: Error,
|
error?: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -32,9 +44,9 @@ export interface AdvancedSearchModalResult {
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface IdSearchModalResult {
|
export interface IdSearchModalResult {
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR,
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
||||||
data?: IdSearchModalData,
|
data?: IdSearchModalData;
|
||||||
error?: Error,
|
error?: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -43,9 +55,9 @@ export interface IdSearchModalResult {
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface SelectModalResult {
|
export interface SelectModalResult {
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.SKIP | ModalResultCode.ERROR,
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.SKIP | ModalResultCode.ERROR;
|
||||||
data?: SelectModalData,
|
data?: SelectModalData;
|
||||||
error?: Error,
|
error?: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,9 +66,19 @@ export interface SelectModalResult {
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface PreviewModalResult {
|
export interface PreviewModalResult {
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR,
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
||||||
data?: PreviewModalData,
|
data?: PreviewModalData;
|
||||||
error?: Error,
|
error?: Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data the search modal returns.
|
||||||
|
* - query: the query string
|
||||||
|
* - types: the selected APIs
|
||||||
|
*/
|
||||||
|
export interface SearchModalData {
|
||||||
|
query: string;
|
||||||
|
types: MediaType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -65,8 +87,8 @@ export interface PreviewModalResult {
|
||||||
* - apis: the selected APIs
|
* - apis: the selected APIs
|
||||||
*/
|
*/
|
||||||
export interface AdvancedSearchModalData {
|
export interface AdvancedSearchModalData {
|
||||||
query: string,
|
query: string;
|
||||||
apis: string[],
|
apis: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -75,8 +97,8 @@ export interface AdvancedSearchModalData {
|
||||||
* - apis: the selected APIs
|
* - apis: the selected APIs
|
||||||
*/
|
*/
|
||||||
export interface IdSearchModalData {
|
export interface IdSearchModalData {
|
||||||
query: string,
|
query: string;
|
||||||
api: string,
|
api: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,7 +106,7 @@ export interface IdSearchModalData {
|
||||||
* - selected: the selected items
|
* - selected: the selected items
|
||||||
*/
|
*/
|
||||||
export interface SelectModalData {
|
export interface SelectModalData {
|
||||||
selected: MediaTypeModel[],
|
selected: MediaTypeModel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -92,7 +114,19 @@ export interface SelectModalData {
|
||||||
* - confirmed: whether the selected element has been confirmed
|
* - confirmed: whether the selected element has been confirmed
|
||||||
*/
|
*/
|
||||||
export interface PreviewModalData {
|
export interface PreviewModalData {
|
||||||
confirmed: boolean,
|
confirmed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the search modal.
|
||||||
|
* - modalTitle: the title of the modal
|
||||||
|
* - preselectedTypes: a list of preselected Types
|
||||||
|
* - prefilledSearchString: prefilled query
|
||||||
|
*/
|
||||||
|
export interface SearchModalOptions {
|
||||||
|
modalTitle?: string;
|
||||||
|
preselectedTypes?: MediaType[];
|
||||||
|
prefilledSearchString?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,9 +136,9 @@ export interface PreviewModalData {
|
||||||
* - prefilledSearchString: prefilled query
|
* - prefilledSearchString: prefilled query
|
||||||
*/
|
*/
|
||||||
export interface AdvancedSearchModalOptions {
|
export interface AdvancedSearchModalOptions {
|
||||||
modalTitle?: string,
|
modalTitle?: string;
|
||||||
preselectedAPIs?: string[],
|
preselectedAPIs?: string[];
|
||||||
prefilledSearchString?: string,
|
prefilledSearchString?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -114,9 +148,9 @@ export interface AdvancedSearchModalOptions {
|
||||||
* - prefilledSearchString: prefilled query
|
* - prefilledSearchString: prefilled query
|
||||||
*/
|
*/
|
||||||
export interface IdSearchModalOptions {
|
export interface IdSearchModalOptions {
|
||||||
modalTitle?: string,
|
modalTitle?: string;
|
||||||
preselectedAPI?: string,
|
preselectedAPI?: string;
|
||||||
prefilledSearchString?: string,
|
prefilledSearchString?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -127,10 +161,10 @@ export interface IdSearchModalOptions {
|
||||||
* - skipButton: whether to add a skip button to the modal
|
* - skipButton: whether to add a skip button to the modal
|
||||||
*/
|
*/
|
||||||
export interface SelectModalOptions {
|
export interface SelectModalOptions {
|
||||||
modalTitle?: string,
|
modalTitle?: string;
|
||||||
elements?: MediaTypeModel[],
|
elements?: MediaTypeModel[];
|
||||||
multiSelect?: boolean,
|
multiSelect?: boolean;
|
||||||
skipButton?: boolean,
|
skipButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -139,11 +173,17 @@ export interface SelectModalOptions {
|
||||||
* - elements: the elements to preview
|
* - elements: the elements to preview
|
||||||
*/
|
*/
|
||||||
export interface PreviewModalOptions {
|
export interface PreviewModalOptions {
|
||||||
modalTitle?: string,
|
modalTitle?: string;
|
||||||
elements?: MediaTypeModel[],
|
elements?: MediaTypeModel[];
|
||||||
createNoteOptions?: CreateNoteOptions,
|
createNoteOptions?: CreateNoteOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SEARCH_MODAL_DEFAULT_OPTIONS: SearchModalOptions = {
|
||||||
|
modalTitle: 'Media DB Search',
|
||||||
|
preselectedTypes: [],
|
||||||
|
prefilledSearchString: '',
|
||||||
|
};
|
||||||
|
|
||||||
export const ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS: AdvancedSearchModalOptions = {
|
export const ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS: AdvancedSearchModalOptions = {
|
||||||
modalTitle: 'Media DB Advanced Search',
|
modalTitle: 'Media DB Advanced Search',
|
||||||
preselectedAPIs: [],
|
preselectedAPIs: [],
|
||||||
|
|
@ -175,11 +215,71 @@ export const PREVIEW_MODAL_DEFAULT_OPTIONS: PreviewModalOptions = {
|
||||||
export class ModalHelper {
|
export class ModalHelper {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin) {
|
constructor(plugin: MediaDbPlugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link MediaDbSearchModal}, then sets callbacks and awaits them,
|
||||||
|
* returning either the user input once submitted or nothing once closed.
|
||||||
|
* The modal needs ot be manually closed by calling `close()` on the modal reference.
|
||||||
|
*
|
||||||
|
* @param searchModalOptions the options for the modal, see {@link SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async createSearchModal(searchModalOptions: SearchModalOptions): Promise<{ searchModalResult: SearchModalResult; searchModal: MediaDbSearchModal }> {
|
||||||
|
const modal = new MediaDbSearchModal(this.plugin, searchModalOptions);
|
||||||
|
const res: SearchModalResult = await new Promise(resolve => {
|
||||||
|
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||||
|
modal.setCloseCallback(err => {
|
||||||
|
if (err) {
|
||||||
|
resolve({ code: ModalResultCode.ERROR, error: err });
|
||||||
|
}
|
||||||
|
resolve({ code: ModalResultCode.CLOSE });
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
return { searchModalResult: res, searchModal: modal };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an {@link MediaDbSearchModal} and awaits its result,
|
||||||
|
* then executes the `submitCallback` returning the callbacks result and closing the modal.
|
||||||
|
*
|
||||||
|
* @param searchModalOptions the options for the modal, see {@link SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async openSearchModal(searchModalOptions: SearchModalOptions, submitCallback: (searchModalData: SearchModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
||||||
|
const { searchModalResult, searchModal } = await this.createSearchModal(searchModalOptions);
|
||||||
|
console.debug(`MDB | searchModal closed with code ${searchModalResult.code}`);
|
||||||
|
|
||||||
|
if (searchModalResult.code === ModalResultCode.ERROR) {
|
||||||
|
// there was an error in the modal itself
|
||||||
|
console.warn(searchModalResult.error);
|
||||||
|
new Notice(searchModalResult.error.toString());
|
||||||
|
searchModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchModalResult.code === ModalResultCode.CLOSE) {
|
||||||
|
// modal is already being closed
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const callbackRes: MediaTypeModel[] = await submitCallback(searchModalResult.data);
|
||||||
|
searchModal.close();
|
||||||
|
return callbackRes;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
new Notice(e.toString());
|
||||||
|
searchModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link MediaDbAdvancedSearchModal}, then sets callbacks and awaits them,
|
* Creates an {@link MediaDbAdvancedSearchModal}, then sets callbacks and awaits them,
|
||||||
* returning either the user input once submitted or nothing once closed.
|
* returning either the user input once submitted or nothing once closed.
|
||||||
|
|
@ -188,9 +288,11 @@ export class ModalHelper {
|
||||||
* @param advancedSearchModalOptions the options for the modal, see {@link ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS}
|
* @param advancedSearchModalOptions the options for the modal, see {@link ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
* @returns the user input or nothing and a reference to the modal.
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
*/
|
*/
|
||||||
async createAdvancedSearchModal(advancedSearchModalOptions: AdvancedSearchModalOptions): Promise<{ advancedSearchModalResult: AdvancedSearchModalResult, advancedSearchModal: MediaDbAdvancedSearchModal }> {
|
async createAdvancedSearchModal(
|
||||||
|
advancedSearchModalOptions: AdvancedSearchModalOptions
|
||||||
|
): Promise<{ advancedSearchModalResult: AdvancedSearchModalResult; advancedSearchModal: MediaDbAdvancedSearchModal }> {
|
||||||
const modal = new MediaDbAdvancedSearchModal(this.plugin, advancedSearchModalOptions);
|
const modal = new MediaDbAdvancedSearchModal(this.plugin, advancedSearchModalOptions);
|
||||||
const res: AdvancedSearchModalResult = await new Promise((resolve, reject) => {
|
const res: AdvancedSearchModalResult = await new Promise(resolve => {
|
||||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||||
modal.setCloseCallback(err => {
|
modal.setCloseCallback(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -212,8 +314,12 @@ export class ModalHelper {
|
||||||
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
||||||
* @returns the user input or nothing and a reference to the modal.
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
*/
|
*/
|
||||||
async openAdvancedSearchModal(advancedSearchModalOptions: AdvancedSearchModalOptions, submitCallback: (advancedSearchModalData: AdvancedSearchModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
async openAdvancedSearchModal(
|
||||||
|
advancedSearchModalOptions: AdvancedSearchModalOptions,
|
||||||
|
submitCallback: (advancedSearchModalData: AdvancedSearchModalData) => Promise<MediaTypeModel[]>
|
||||||
|
): Promise<MediaTypeModel[]> {
|
||||||
const { advancedSearchModalResult, advancedSearchModal } = await this.createAdvancedSearchModal(advancedSearchModalOptions);
|
const { advancedSearchModalResult, advancedSearchModal } = await this.createAdvancedSearchModal(advancedSearchModalOptions);
|
||||||
|
console.debug(`MDB | advencedSearchModal closed with code ${advancedSearchModalResult.code}`);
|
||||||
|
|
||||||
if (advancedSearchModalResult.code === ModalResultCode.ERROR) {
|
if (advancedSearchModalResult.code === ModalResultCode.ERROR) {
|
||||||
// there was an error in the modal itself
|
// there was an error in the modal itself
|
||||||
|
|
@ -229,8 +335,7 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let callbackRes: MediaTypeModel[];
|
const callbackRes: MediaTypeModel[] = await submitCallback(advancedSearchModalResult.data);
|
||||||
callbackRes = await submitCallback(advancedSearchModalResult.data);
|
|
||||||
advancedSearchModal.close();
|
advancedSearchModal.close();
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -249,9 +354,9 @@ export class ModalHelper {
|
||||||
* @param idSearchModalOptions the options for the modal, see {@link ID_SEARCH_MODAL_DEFAULT_OPTIONS}
|
* @param idSearchModalOptions the options for the modal, see {@link ID_SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
* @returns the user input or nothing and a reference to the modal.
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
*/
|
*/
|
||||||
async createIdSearchModal(idSearchModalOptions: IdSearchModalOptions): Promise<{ idSearchModalResult: IdSearchModalResult, idSearchModal: MediaDbIdSearchModal }> {
|
async createIdSearchModal(idSearchModalOptions: IdSearchModalOptions): Promise<{ idSearchModalResult: IdSearchModalResult; idSearchModal: MediaDbIdSearchModal }> {
|
||||||
const modal = new MediaDbIdSearchModal(this.plugin, idSearchModalOptions);
|
const modal = new MediaDbIdSearchModal(this.plugin, idSearchModalOptions);
|
||||||
const res: IdSearchModalResult = await new Promise((resolve, reject) => {
|
const res: IdSearchModalResult = await new Promise(resolve => {
|
||||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||||
modal.setCloseCallback(err => {
|
modal.setCloseCallback(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -273,8 +378,12 @@ export class ModalHelper {
|
||||||
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
||||||
* @returns the user input or nothing and a reference to the modal.
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
*/
|
*/
|
||||||
async openIdSearchModal(idSearchModalOptions: IdSearchModalOptions, submitCallback: (idSearchModalData: IdSearchModalData) => Promise<MediaTypeModel>): Promise<MediaTypeModel> {
|
async openIdSearchModal(
|
||||||
|
idSearchModalOptions: IdSearchModalOptions,
|
||||||
|
submitCallback: (idSearchModalData: IdSearchModalData) => Promise<MediaTypeModel>
|
||||||
|
): Promise<MediaTypeModel> {
|
||||||
const { idSearchModalResult, idSearchModal } = await this.createIdSearchModal(idSearchModalOptions);
|
const { idSearchModalResult, idSearchModal } = await this.createIdSearchModal(idSearchModalOptions);
|
||||||
|
console.debug(`MDB | idSearchModal closed with code ${idSearchModalResult.code}`);
|
||||||
|
|
||||||
if (idSearchModalResult.code === ModalResultCode.ERROR) {
|
if (idSearchModalResult.code === ModalResultCode.ERROR) {
|
||||||
// there was an error in the modal itself
|
// there was an error in the modal itself
|
||||||
|
|
@ -290,8 +399,7 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let callbackRes: MediaTypeModel;
|
const callbackRes: MediaTypeModel = await submitCallback(idSearchModalResult.data);
|
||||||
callbackRes = await submitCallback(idSearchModalResult.data);
|
|
||||||
idSearchModal.close();
|
idSearchModal.close();
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -310,9 +418,9 @@ export class ModalHelper {
|
||||||
* @param selectModalOptions the options for the modal, see {@link SELECT_MODAL_OPTIONS_DEFAULT}
|
* @param selectModalOptions the options for the modal, see {@link SELECT_MODAL_OPTIONS_DEFAULT}
|
||||||
* @returns the user input or nothing and a reference to the modal.
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
*/
|
*/
|
||||||
async createSelectModal(selectModalOptions: SelectModalOptions): Promise<{ selectModalResult: SelectModalResult, selectModal: MediaDbSearchResultModal }> {
|
async createSelectModal(selectModalOptions: SelectModalOptions): Promise<{ selectModalResult: SelectModalResult; selectModal: MediaDbSearchResultModal }> {
|
||||||
const modal = new MediaDbSearchResultModal(this.plugin, selectModalOptions);
|
const modal = new MediaDbSearchResultModal(this.plugin, selectModalOptions);
|
||||||
const res: SelectModalResult = await new Promise((resolve, reject) => {
|
const res: SelectModalResult = await new Promise(resolve => {
|
||||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||||
modal.setSkipCallback(() => resolve({ code: ModalResultCode.SKIP }));
|
modal.setSkipCallback(() => resolve({ code: ModalResultCode.SKIP }));
|
||||||
modal.setCloseCallback(err => {
|
modal.setCloseCallback(err => {
|
||||||
|
|
@ -337,6 +445,7 @@ export class ModalHelper {
|
||||||
*/
|
*/
|
||||||
async openSelectModal(selectModalOptions: SelectModalOptions, submitCallback: (selectModalData: SelectModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
async openSelectModal(selectModalOptions: SelectModalOptions, submitCallback: (selectModalData: SelectModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
||||||
const { selectModalResult, selectModal } = await this.createSelectModal(selectModalOptions);
|
const { selectModalResult, selectModal } = await this.createSelectModal(selectModalOptions);
|
||||||
|
console.debug(`MDB | selectModal closed with code ${selectModalResult.code}`);
|
||||||
|
|
||||||
if (selectModalResult.code === ModalResultCode.ERROR) {
|
if (selectModalResult.code === ModalResultCode.ERROR) {
|
||||||
// there was an error in the modal itself
|
// there was an error in the modal itself
|
||||||
|
|
@ -357,8 +466,7 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let callbackRes: MediaTypeModel[];
|
const callbackRes: MediaTypeModel[] = await submitCallback(selectModalResult.data);
|
||||||
callbackRes = await submitCallback(selectModalResult.data);
|
|
||||||
selectModal.close();
|
selectModal.close();
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -369,10 +477,10 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPreviewModal(previewModalOptions: PreviewModalOptions): Promise<{ previewModalResult: PreviewModalResult, previewModal: MediaDbPreviewModal }> {
|
async createPreviewModal(previewModalOptions: PreviewModalOptions): Promise<{ previewModalResult: PreviewModalResult; previewModal: MediaDbPreviewModal }> {
|
||||||
//todo: handle attachFile for existing files
|
//todo: handle attachFile for existing files
|
||||||
const modal = new MediaDbPreviewModal(this.plugin, previewModalOptions);
|
const modal = new MediaDbPreviewModal(this.plugin, previewModalOptions);
|
||||||
const res: PreviewModalResult = await new Promise((resolve, reject) => {
|
const res: PreviewModalResult = await new Promise(resolve => {
|
||||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||||
modal.setCloseCallback(err => {
|
modal.setCloseCallback(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -388,6 +496,7 @@ export class ModalHelper {
|
||||||
|
|
||||||
async openPreviewModal(previewModalOptions: PreviewModalOptions, submitCallback: (previewModalData: PreviewModalData) => Promise<boolean>): Promise<boolean> {
|
async openPreviewModal(previewModalOptions: PreviewModalOptions, submitCallback: (previewModalData: PreviewModalData) => Promise<boolean>): Promise<boolean> {
|
||||||
const { previewModalResult, previewModal } = await this.createPreviewModal(previewModalOptions);
|
const { previewModalResult, previewModal } = await this.createPreviewModal(previewModalOptions);
|
||||||
|
console.debug(`MDB | previewModal closed with code ${previewModalResult.code}`);
|
||||||
|
|
||||||
if (previewModalResult.code === ModalResultCode.ERROR) {
|
if (previewModalResult.code === ModalResultCode.ERROR) {
|
||||||
// there was an error in the modal itself
|
// there was an error in the modal itself
|
||||||
|
|
@ -403,8 +512,7 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let callbackRes: boolean;
|
const callbackRes: boolean = await submitCallback(previewModalResult.data);
|
||||||
callbackRes = await submitCallback(previewModalResult.data);
|
|
||||||
previewModal.close();
|
previewModal.close();
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -415,4 +523,3 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -6,14 +6,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-db-plugin-list-toggle {
|
.media-db-plugin-list-toggle {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-db-plugin-list-text-wrapper {
|
.media-db-plugin-list-text-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.media-db-plugin-list-text {
|
.media-db-plugin-list-text {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": [
|
"types": ["svelte", "node", "jest"],
|
||||||
"svelte",
|
|
||||||
"node",
|
|
||||||
"jest"
|
|
||||||
],
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"inlineSourceMap": true,
|
"inlineSourceMap": true,
|
||||||
"inlineSources": true,
|
"inlineSources": true,
|
||||||
|
|
@ -17,14 +13,7 @@
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"lib": [
|
"lib": ["DOM", "ES5", "ES6", "ES7"]
|
||||||
"DOM",
|
|
||||||
"ES5",
|
|
||||||
"ES6",
|
|
||||||
"ES7"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["**/*.ts"]
|
||||||
"**/*.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { readFileSync, writeFileSync } from "fs";
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
|
||||||
const targetVersion = process.env.npm_package_version;
|
const targetVersion = process.env.npm_package_version;
|
||||||
|
|
||||||
// read minAppVersion from manifest.json and bump version to target version
|
// read minAppVersion from manifest.json and bump version to target version
|
||||||
let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
|
let manifest = JSON.parse(readFileSync('manifest.json', 'utf8'));
|
||||||
const { minAppVersion } = manifest;
|
const { minAppVersion } = manifest;
|
||||||
manifest.version = targetVersion;
|
manifest.version = targetVersion;
|
||||||
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
|
writeFileSync('manifest.json', JSON.stringify(manifest, null, '\t'));
|
||||||
|
|
||||||
// update versions.json with target version and minAppVersion from manifest.json
|
// update versions.json with target version and minAppVersion from manifest.json
|
||||||
let versions = JSON.parse(readFileSync("versions.json", "utf8"));
|
let versions = JSON.parse(readFileSync('versions.json', 'utf8'));
|
||||||
versions[targetVersion] = minAppVersion;
|
versions[targetVersion] = minAppVersion;
|
||||||
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));
|
writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue