Merge branch 'master' of https://github.com/mProjectsCode/obsidian-media-db-plugin
This commit is contained in:
commit
ed78ffcd64
33 changed files with 468 additions and 364 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
"root": true,
|
"root": true,
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"env": { "node": true },
|
"env": { "node": true },
|
||||||
"plugins": ["@typescript-eslint"],
|
"plugins": ["@typescript-eslint", "only-warn"],
|
||||||
"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"
|
||||||
|
|
@ -12,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"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
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 @@ Conveying under any other circumstances is permitted solely under
|
||||||
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 @@ Additional terms, permissive or non-permissive, may be stated in the
|
||||||
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 @@ Nothing in this License shall be construed as excluding or limiting
|
||||||
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>.
|
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,15 @@ You are more than welcome to open an issue on [GitHub](https://github.com/mProje
|
||||||
|
|
||||||
### 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)
|
||||||
|
|
|
||||||
|
|
@ -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.1",
|
||||||
"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",
|
||||||
|
|
|
||||||
16
package-lock.json
generated
16
package-lock.json
generated
|
|
@ -22,6 +22,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",
|
||||||
|
|
@ -2664,6 +2665,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",
|
||||||
|
|
@ -7649,6 +7659,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",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-media-db-plugin",
|
"name": "obsidian-media-db-plugin",
|
||||||
"version": "0.4.1",
|
"version": "0.5.1",
|
||||||
"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": {
|
||||||
|
|
@ -23,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",
|
||||||
|
|
|
||||||
|
|
@ -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,13 +33,13 @@ 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(
|
ret.push(
|
||||||
|
|
@ -70,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,
|
||||||
|
|
@ -89,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: {
|
||||||
|
|
|
||||||
|
|
@ -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,15 +30,15 @@ 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());
|
||||||
|
|
@ -85,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) {
|
||||||
|
|
@ -93,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());
|
||||||
|
|
@ -111,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,
|
||||||
|
|
@ -140,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,
|
||||||
|
|
@ -168,6 +172,7 @@ 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,
|
||||||
|
|
|
||||||
|
|
@ -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,15 +31,15 @@ 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(
|
ret.push(
|
||||||
|
|
@ -76,7 +77,7 @@ 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({
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
@ -40,15 +40,19 @@ export class OMDbAPI extends APIModel {
|
||||||
const data = await fetchData.json();
|
const data = await fetchData.json();
|
||||||
|
|
||||||
if (data.Response === 'False') {
|
if (data.Response === 'False') {
|
||||||
throw Error(`MDB | Received error from ${this.apiName}: ${data.Error}`);
|
if (data.Error === 'Movie not found!') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(`MDB | Received error from ${this.apiName}: \n${JSON.stringify(data, undefined, 4)}`);
|
||||||
}
|
}
|
||||||
if (!data.Search) {
|
if (!data.Search) {
|
||||||
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());
|
||||||
|
|
@ -108,7 +112,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}`);
|
||||||
|
|
@ -133,9 +137,11 @@ 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,
|
||||||
|
streamingServices: [],
|
||||||
premiere: new Date(result.Released).toLocaleDateString() ?? 'unknown',
|
premiere: new Date(result.Released).toLocaleDateString() ?? 'unknown',
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
|
|
@ -161,9 +167,11 @@ 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',
|
||||||
|
|
|
||||||
|
|
@ -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,7 +48,7 @@ export class SteamAPI extends APIModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const result of filteredData) {
|
for (const result of filteredData) {
|
||||||
ret.push(
|
ret.push(
|
||||||
|
|
@ -70,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,
|
||||||
});
|
});
|
||||||
|
|
@ -79,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)) {
|
||||||
|
|
@ -94,7 +93,7 @@ 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,
|
||||||
|
|
|
||||||
|
|
@ -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,15 +22,15 @@ 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(
|
ret.push(
|
||||||
|
|
@ -59,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({
|
||||||
|
|
|
||||||
76
src/main.ts
76
src/main.ts
|
|
@ -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,9 +45,10 @@ 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) => this.createEntryWithAdvancedSearchModal());
|
const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', () => this.createEntryWithAdvancedSearchModal());
|
||||||
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
||||||
|
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
|
|
@ -135,8 +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> {
|
||||||
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -162,7 +163,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntryWithSearchModal(searchModalOptions?: SearchModalOptions) {
|
async createEntryWithSearchModal(searchModalOptions?: SearchModalOptions): Promise<void> {
|
||||||
let types: string[] = [];
|
let types: string[] = [];
|
||||||
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openSearchModal(searchModalOptions ?? {}, async searchModalData => {
|
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openSearchModal(searchModalOptions ?? {}, async searchModalData => {
|
||||||
types = searchModalData.types;
|
types = searchModalData.types;
|
||||||
|
|
@ -197,8 +198,8 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
await this.createMediaDbNotes(selectResults);
|
await this.createMediaDbNotes(selectResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntryWithAdvancedSearchModal() {
|
async createEntryWithAdvancedSearchModal(): Promise<void> {
|
||||||
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -253,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));
|
||||||
|
|
@ -269,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) {
|
||||||
|
|
@ -278,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) : '';
|
||||||
|
|
@ -295,7 +300,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
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);
|
||||||
|
|
@ -312,7 +317,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
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);
|
||||||
|
|
@ -348,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
|
||||||
|
|
@ -366,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(this.settings.folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
fileName = replaceIllegalFileNameCharactersInString(fileName);
|
fileName = replaceIllegalFileNameCharactersInString(fileName);
|
||||||
const filePath = `${folder.path}/${fileName}.md`;
|
const filePath = `${folder.path}/${fileName}.md`;
|
||||||
|
|
@ -401,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');
|
||||||
|
|
@ -416,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);
|
||||||
|
|
@ -436,11 +438,11 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
@ -454,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -474,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 });
|
||||||
|
|
@ -513,35 +515,34 @@ 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 (newPropertyMappingModel === undefined) {
|
||||||
// if the propertyMappingModel exists in the default settings but not the loaded settings, add it
|
// if the propertyMappingModel exists in the default settings but not the loaded settings, add it
|
||||||
newPropertyMappings.push(defaultPropertyMappingModel);
|
newPropertyMappings.push(defaultPropertyMappingModel);
|
||||||
} else {
|
} else {
|
||||||
// if the propertyMappingModel also exists in the loaded settings, add it from there
|
// if the propertyMappingModel also exists in the loaded settings, add it from there
|
||||||
let newProperties: PropertyMapping[] = [];
|
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);
|
||||||
|
|
@ -559,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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,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();
|
||||||
}
|
}
|
||||||
|
|
@ -66,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 });
|
||||||
|
|
@ -123,7 +123,7 @@ 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' });
|
||||||
|
|
@ -90,7 +90,7 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose(): void {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,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();
|
||||||
}
|
}
|
||||||
|
|
@ -59,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 });
|
||||||
|
|
@ -108,7 +108,7 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback();
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,10 @@
|
||||||
import { ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
import { ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../main';
|
import MediaDbPlugin from '../main';
|
||||||
import {
|
import { SEARCH_MODAL_DEFAULT_OPTIONS, SearchModalData, SearchModalOptions } from '../utils/ModalHelper';
|
||||||
ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS,
|
|
||||||
AdvancedSearchModalData,
|
|
||||||
AdvancedSearchModalOptions,
|
|
||||||
SEARCH_MODAL_DEFAULT_OPTIONS,
|
|
||||||
SearchModalData,
|
|
||||||
SearchModalOptions,
|
|
||||||
} from '../utils/ModalHelper';
|
|
||||||
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||||
import { unCamelCase } from '../utils/Utils';
|
import { unCamelCase } from '../utils/Utils';
|
||||||
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
export class MediaDbSearchModal extends Modal {
|
export class MediaDbSearchModal extends Modal {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -18,7 +12,7 @@ export class MediaDbSearchModal extends Modal {
|
||||||
query: string;
|
query: string;
|
||||||
isBusy: boolean;
|
isBusy: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
selectedTypes: { name: string; selected: boolean }[];
|
selectedTypes: { name: MediaType; selected: boolean }[];
|
||||||
|
|
||||||
searchBtn: ButtonComponent;
|
searchBtn: ButtonComponent;
|
||||||
|
|
||||||
|
|
@ -47,7 +41,7 @@ export class MediaDbSearchModal 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();
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +53,7 @@ export class MediaDbSearchModal extends Modal {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const types: string[] = this.selectedTypes.filter(x => x.selected).map(x => x.name);
|
const types: MediaType[] = this.selectedTypes.filter(x => x.selected).map(x => x.name);
|
||||||
|
|
||||||
if (types.length === 0) {
|
if (types.length === 0) {
|
||||||
new Notice('MDB | No Type selected');
|
new Notice('MDB | No Type selected');
|
||||||
|
|
@ -75,7 +69,7 @@ export class MediaDbSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpen() {
|
onOpen(): void {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
|
|
||||||
contentEl.createEl('h2', { text: this.title });
|
contentEl.createEl('h2', { text: this.title });
|
||||||
|
|
@ -130,7 +124,7 @@ export class MediaDbSearchModal extends Modal {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback();
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
|
|
|
||||||
|
|
@ -40,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...');
|
||||||
|
|
@ -55,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,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);
|
||||||
|
|
@ -67,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);
|
||||||
|
|
@ -75,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 });
|
||||||
|
|
@ -122,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());
|
||||||
|
|
@ -133,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);
|
||||||
|
|
@ -145,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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,11 @@ 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;
|
||||||
|
|
@ -19,8 +24,15 @@ export class BoardGameModel extends MediaTypeModel {
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ export class MovieModel extends MediaTypeModel {
|
||||||
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: {
|
||||||
|
|
@ -25,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,
|
||||||
|
|
|
||||||
|
|
@ -17,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;
|
||||||
|
|
@ -38,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,
|
||||||
|
|
|
||||||
|
|
@ -40,6 +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;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ 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;
|
||||||
|
|
@ -36,11 +35,17 @@ export interface MediaDbPluginSettings {
|
||||||
musicReleasePropertyConversionRules: string;
|
musicReleasePropertyConversionRules: string;
|
||||||
boardgamePropertyConversionRules: string;
|
boardgamePropertyConversionRules: string;
|
||||||
|
|
||||||
|
movieFolder: string;
|
||||||
|
seriesFolder: string;
|
||||||
|
gameFolder: string;
|
||||||
|
wikiFolder: string;
|
||||||
|
musicReleaseFolder: 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,
|
||||||
|
|
@ -67,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[] = [];
|
||||||
|
|
@ -111,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".')
|
||||||
|
|
@ -166,6 +165,87 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
|
|
@ -326,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>
|
||||||
|
|
@ -345,8 +425,8 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
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) {
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ 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[] = [];
|
||||||
|
|
||||||
|
|
@ -68,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];
|
||||||
|
|
@ -141,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
|
||||||
|
|
|
||||||
|
|
@ -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,10 +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);
|
||||||
|
|
@ -36,11 +37,21 @@ 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) {
|
||||||
|
|
@ -61,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.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ 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 { MediaDbSearchModal } from '../modals/MediaDbSearchModal';
|
||||||
|
import { MediaType } from './MediaType';
|
||||||
|
|
||||||
export enum ModalResultCode {
|
export enum ModalResultCode {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
|
|
@ -77,7 +78,7 @@ export interface PreviewModalResult {
|
||||||
*/
|
*/
|
||||||
export interface SearchModalData {
|
export interface SearchModalData {
|
||||||
query: string;
|
query: string;
|
||||||
types: string[];
|
types: MediaType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -124,7 +125,7 @@ export interface PreviewModalData {
|
||||||
*/
|
*/
|
||||||
export interface SearchModalOptions {
|
export interface SearchModalOptions {
|
||||||
modalTitle?: string;
|
modalTitle?: string;
|
||||||
preselectedTypes?: string[];
|
preselectedTypes?: MediaType[];
|
||||||
prefilledSearchString?: string;
|
prefilledSearchString?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +229,7 @@ export class ModalHelper {
|
||||||
*/
|
*/
|
||||||
async createSearchModal(searchModalOptions: SearchModalOptions): Promise<{ searchModalResult: SearchModalResult; searchModal: MediaDbSearchModal }> {
|
async createSearchModal(searchModalOptions: SearchModalOptions): Promise<{ searchModalResult: SearchModalResult; searchModal: MediaDbSearchModal }> {
|
||||||
const modal = new MediaDbSearchModal(this.plugin, searchModalOptions);
|
const modal = new MediaDbSearchModal(this.plugin, searchModalOptions);
|
||||||
const res: SearchModalResult = await new Promise((resolve, reject) => {
|
const res: SearchModalResult = 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) {
|
||||||
|
|
@ -268,8 +269,7 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let callbackRes: MediaTypeModel[];
|
const callbackRes: MediaTypeModel[] = await submitCallback(searchModalResult.data);
|
||||||
callbackRes = await submitCallback(searchModalResult.data);
|
|
||||||
searchModal.close();
|
searchModal.close();
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -292,7 +292,7 @@ export class ModalHelper {
|
||||||
advancedSearchModalOptions: AdvancedSearchModalOptions
|
advancedSearchModalOptions: AdvancedSearchModalOptions
|
||||||
): Promise<{ advancedSearchModalResult: AdvancedSearchModalResult; advancedSearchModal: MediaDbAdvancedSearchModal }> {
|
): 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) {
|
||||||
|
|
@ -335,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) {
|
||||||
|
|
@ -357,7 +356,7 @@ export class ModalHelper {
|
||||||
*/
|
*/
|
||||||
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) {
|
||||||
|
|
@ -400,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) {
|
||||||
|
|
@ -422,7 +420,7 @@ export class ModalHelper {
|
||||||
*/
|
*/
|
||||||
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 => {
|
||||||
|
|
@ -468,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) {
|
||||||
|
|
@ -483,7 +480,7 @@ 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) {
|
||||||
|
|
@ -515,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) {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { TFile, TFolder } from 'obsidian';
|
||||||
export const pluginName: string = 'obsidian-media-db-plugin';
|
export const pluginName: string = 'obsidian-media-db-plugin';
|
||||||
export const contactEmail: string = 'm.projects.code@gmail.com';
|
export const contactEmail: string = 'm.projects.code@gmail.com';
|
||||||
export const mediaDbTag: string = 'mediaDB';
|
export const mediaDbTag: string = 'mediaDB';
|
||||||
export const mediaDbVersion: string = '0.4.1';
|
export const mediaDbVersion: string = '0.5.0';
|
||||||
export const debug: boolean = true;
|
export const debug: boolean = true;
|
||||||
|
|
||||||
export function wrapAround(value: number, size: number): number {
|
export function wrapAround(value: number, size: number): number {
|
||||||
|
|
@ -14,16 +14,6 @@ export function wrapAround(value: number, size: number): number {
|
||||||
return mod(value, size);
|
return mod(value, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use console.debug instead
|
|
||||||
* @param o
|
|
||||||
*/
|
|
||||||
export function debugLog(o: any): void {
|
|
||||||
if (debug) {
|
|
||||||
console.log(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function containsOnlyLettersAndUnderscores(str: string): boolean {
|
export function containsOnlyLettersAndUnderscores(str: string): boolean {
|
||||||
return /^[a-zA-Z_]+$/.test(str);
|
return /^[a-zA-Z_]+$/.test(str);
|
||||||
}
|
}
|
||||||
|
|
@ -33,9 +23,7 @@ export function replaceIllegalFileNameCharactersInString(string: string): string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replaceTags(template: string, mediaTypeModel: MediaTypeModel): string {
|
export function replaceTags(template: string, mediaTypeModel: MediaTypeModel): string {
|
||||||
const resolvedTemplate = template.replace(new RegExp('{{.*?}}', 'g'), (match: string) => replaceTag(match, mediaTypeModel));
|
return template.replace(new RegExp('{{.*?}}', 'g'), (match: string) => replaceTag(match, mediaTypeModel));
|
||||||
|
|
||||||
return resolvedTemplate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
|
function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
|
||||||
|
|
@ -44,11 +32,11 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
|
||||||
tag = tag.substring(0, tag.length - 2);
|
tag = tag.substring(0, tag.length - 2);
|
||||||
tag = tag.trim();
|
tag = tag.trim();
|
||||||
|
|
||||||
let parts = tag.split(':');
|
const parts = tag.split(':');
|
||||||
if (parts.length === 1) {
|
if (parts.length === 1) {
|
||||||
let path = parts[0].split('.');
|
const path = parts[0].split('.');
|
||||||
|
|
||||||
let obj = traverseMetaData(path, mediaTypeModel);
|
const obj = traverseMetaData(path, mediaTypeModel);
|
||||||
|
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
return '{{ INVALID TEMPLATE TAG - object undefined }}';
|
return '{{ INVALID TEMPLATE TAG - object undefined }}';
|
||||||
|
|
@ -56,11 +44,11 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
} else if (parts.length === 2) {
|
} else if (parts.length === 2) {
|
||||||
let operator = parts[0];
|
const operator = parts[0];
|
||||||
|
|
||||||
let path = parts[1].split('.');
|
const path = parts[1].split('.');
|
||||||
|
|
||||||
let obj = traverseMetaData(path, mediaTypeModel);
|
const obj = traverseMetaData(path, mediaTypeModel);
|
||||||
|
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
return '{{ INVALID TEMPLATE TAG - object undefined }}';
|
return '{{ INVALID TEMPLATE TAG - object undefined }}';
|
||||||
|
|
@ -87,7 +75,7 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
|
||||||
function traverseMetaData(path: Array<string>, mediaTypeModel: MediaTypeModel): any {
|
function traverseMetaData(path: Array<string>, mediaTypeModel: MediaTypeModel): any {
|
||||||
let o: any = mediaTypeModel;
|
let o: any = mediaTypeModel;
|
||||||
|
|
||||||
for (let part of path) {
|
for (const part of path) {
|
||||||
if (o !== undefined) {
|
if (o !== undefined) {
|
||||||
o = o[part];
|
o = o[part];
|
||||||
}
|
}
|
||||||
|
|
@ -97,12 +85,12 @@ function traverseMetaData(path: Array<string>, mediaTypeModel: MediaTypeModel):
|
||||||
}
|
}
|
||||||
|
|
||||||
export function markdownTable(content: string[][]): string {
|
export function markdownTable(content: string[][]): string {
|
||||||
let rows = content.length;
|
const rows = content.length;
|
||||||
if (rows === 0) {
|
if (rows === 0) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
let columns = content[0].length;
|
const columns = content[0].length;
|
||||||
if (columns === 0) {
|
if (columns === 0) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +100,7 @@ export function markdownTable(content: string[][]): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let longestStringInColumns: number[] = [];
|
const longestStringInColumns: number[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < columns; i++) {
|
for (let i = 0; i < columns; i++) {
|
||||||
let longestStringInColumn = 0;
|
let longestStringInColumn = 0;
|
||||||
|
|
@ -147,36 +135,18 @@ export function markdownTable(content: string[][]): string {
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dateToString(date: Date) {
|
export function dateToString(date: Date): string {
|
||||||
return `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`;
|
return `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function timeToString(time: Date) {
|
export function timeToString(time: Date): string {
|
||||||
return `${time.getHours()}-${time.getMinutes()}-${time.getSeconds()}`;
|
return `${time.getHours()}-${time.getMinutes()}-${time.getSeconds()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dateTimeToString(dateTime: Date) {
|
export function dateTimeToString(dateTime: Date): string {
|
||||||
return `${dateToString(dateTime)} ${timeToString(dateTime)}`;
|
return `${dateToString(dateTime)} ${timeToString(dateTime)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export class UserCancelError extends Error {
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export class UserSkipError extends Error {
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// js can't even implement modulo correctly...
|
// js can't even implement modulo correctly...
|
||||||
export function mod(n: number, m: number): number {
|
export function mod(n: number, m: number): number {
|
||||||
return ((n % m) + m) % m;
|
return ((n % m) + m) % m;
|
||||||
|
|
@ -202,6 +172,7 @@ export class PropertyMappingNameConflictError extends Error {
|
||||||
* - attachTemplate: whether to attach the template (DEFAULT: false)
|
* - attachTemplate: whether to attach the template (DEFAULT: false)
|
||||||
* - attachFie: a file to attach (DEFAULT: undefined)
|
* - attachFie: a file to attach (DEFAULT: undefined)
|
||||||
* - openNote: whether to open the note after creation (DEFAULT: false)
|
* - openNote: whether to open the note after creation (DEFAULT: false)
|
||||||
|
* - folder: folder to put the note in
|
||||||
*/
|
*/
|
||||||
export interface CreateNoteOptions {
|
export interface CreateNoteOptions {
|
||||||
attachTemplate?: boolean;
|
attachTemplate?: boolean;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue