1- import { FrontMatterCache , Notice , Plugin , TFile } from 'obsidian' ;
1+ import { Notice , Plugin , TFile , TFolder } from 'obsidian' ;
22import { DEFAULT_SETTINGS , MediaDbPluginSettings , MediaDbSettingTab } from './settings/Settings' ;
33import { APIManager } from './api/APIManager' ;
44import { MediaTypeModel } from './models/MediaTypeModel' ;
5- import { replaceIllegalFileNameCharactersInString } from './utils/Utils' ;
5+ import { dateTimeToString , markdownTable , replaceIllegalFileNameCharactersInString } from './utils/Utils' ;
66import { OMDbAPI } from './api/apis/OMDbAPI' ;
77import { MediaDbAdvancedSearchModal } from './modals/MediaDbAdvancedSearchModal' ;
88import { MediaDbSearchResultModal } from './modals/MediaDbSearchResultModal' ;
@@ -14,6 +14,7 @@ import {MediaTypeManager} from './utils/MediaTypeManager';
1414import { SteamAPI } from './api/apis/SteamAPI' ;
1515import { ModelPropertyMapper } from './settings/ModelPropertyMapper' ;
1616import { YAMLConverter } from './utils/YAMLConverter' ;
17+ import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal' ;
1718
1819export default class MediaDbPlugin extends Plugin {
1920 settings : MediaDbPluginSettings ;
@@ -30,6 +31,16 @@ export default class MediaDbPlugin extends Plugin {
3031 ) ;
3132 ribbonIconEl . addClass ( 'obsidian-media-db-plugin-ribbon-class' ) ;
3233
34+ this . registerEvent ( this . app . workspace . on ( 'file-menu' , ( menu , file ) => {
35+ if ( file instanceof TFolder ) {
36+ menu . addItem ( item => {
37+ item . setTitle ( 'Create Media DB entries from folder' )
38+ . setIcon ( 'database' )
39+ . onClick ( ( ) => this . createEntriesFromFolder ( file as TFolder ) ) ;
40+ } ) ;
41+ }
42+ } ) ) ;
43+
3344 // register command to open search modal
3445 this . addCommand ( {
3546 id : 'open-media-db-search-modal' ,
@@ -74,16 +85,23 @@ export default class MediaDbPlugin extends Plugin {
7485 this . modelPropertyMapper = new ModelPropertyMapper ( this . settings ) ;
7586 }
7687
77- async createMediaDbNote ( modal : ( ) => Promise < MediaTypeModel > ) : Promise < void > {
88+ async createMediaDbNote ( modal : ( ) => Promise < MediaTypeModel [ ] > ) : Promise < void > {
89+ let models : MediaTypeModel [ ] = [ ] ;
7890 try {
79- let data : MediaTypeModel = await modal ( ) ;
80- data = await this . apiManager . queryDetailedInfo ( data ) ;
81-
82- await this . createMediaDbNoteFromModel ( data ) ;
91+ models = await modal ( ) ;
8392 } catch ( e ) {
8493 console . warn ( e ) ;
8594 new Notice ( e . toString ( ) ) ;
8695 }
96+
97+ for ( const model of models ) {
98+ try {
99+ await this . createMediaDbNoteFromModel ( await this . apiManager . queryDetailedInfo ( model ) ) ;
100+ } catch ( e ) {
101+ console . warn ( e ) ;
102+ new Notice ( e . toString ( ) ) ;
103+ }
104+ }
87105 }
88106
89107 async createMediaDbNoteFromModel ( mediaTypeModel : MediaTypeModel ) : Promise < void > {
@@ -126,44 +144,23 @@ export default class MediaDbPlugin extends Plugin {
126144 }
127145 }
128146
129- async openMediaDbAdvancedSearchModal ( ) : Promise < MediaTypeModel > {
130- return new Promise ( ( ( resolve , reject ) => {
131- new MediaDbAdvancedSearchModal ( this . app , this , ( err , results ) => {
132- if ( err ) return reject ( err ) ;
133- new MediaDbSearchResultModal ( this . app , this , results , ( err2 , res ) => {
134- if ( err2 ) return reject ( err2 ) ;
135- resolve ( res ) ;
136- } ) . open ( ) ;
137- } ) . open ( ) ;
138- } ) ) ;
139- }
140-
141- async openMediaDbIdSearchModal ( ) : Promise < MediaTypeModel > {
142- return new Promise ( ( ( resolve , reject ) => {
143- new MediaDbIdSearchModal ( this . app , this , ( err , res ) => {
144- if ( err ) return reject ( err ) ;
145- resolve ( res ) ;
146- } ) . open ( ) ;
147- } ) ) ;
148- }
149-
150147 async updateActiveNote ( ) {
151148 const activeFile : TFile = this . app . workspace . getActiveFile ( ) ;
152149 if ( ! activeFile ) {
153150 throw new Error ( 'MDB | there is no active note' ) ;
154151 }
155152
156153 let metadata : any = this . app . metadataCache . getFileCache ( activeFile ) . frontmatter ;
154+ metadata = JSON . parse ( JSON . stringify ( metadata ) ) ; // deep copy
157155 delete metadata . position ; // remove unnecessary data from the FrontMatterCache
158156 metadata = this . modelPropertyMapper . convertObjectBack ( metadata ) ;
159157
160- console . log ( metadata )
158+ console . log ( metadata ) ;
161159
162160 if ( ! metadata ?. type || ! metadata ?. dataSource || ! metadata ?. id ) {
163161 throw new Error ( 'MDB | active note is not a Media DB entry or is missing metadata' ) ;
164162 }
165163
166-
167164 let oldMediaTypeModel = this . mediaTypeManager . createMediaTypeModelFromMediaType ( metadata , metadata . type ) ;
168165
169166 let newMediaTypeModel = await this . apiManager . queryDetailedInfoById ( metadata . id , metadata . dataSource ) ;
@@ -178,6 +175,94 @@ export default class MediaDbPlugin extends Plugin {
178175 await this . createMediaDbNoteFromModel ( newMediaTypeModel ) ;
179176 }
180177
178+ async createEntriesFromFolder ( folder : TFolder ) {
179+ const erroredFiles : { filePath : string , error : string } [ ] = [ ] ;
180+
181+ const { selectedAPI, titleFieldName} = await new Promise ( ( resolve , reject ) => {
182+ new MediaDbFolderImportModal ( this . app , this , ( ( selectedAPI , titleFieldName ) => {
183+ resolve ( { selectedAPI, titleFieldName} ) ;
184+ } ) ) . open ( ) ;
185+ } ) ;
186+
187+ const selectedAPIs = { } ;
188+ for ( const api of this . apiManager . apis ) {
189+ // @ts -ignore
190+ selectedAPIs [ api . apiName ] = api . apiName === selectedAPI ;
191+ }
192+
193+ for ( const child of folder . children ) {
194+ if ( child instanceof TFile ) {
195+ const file = child as TFile ;
196+ let metadata : any = this . app . metadataCache . getFileCache ( file ) . frontmatter ;
197+
198+ let title = metadata [ titleFieldName ] ;
199+ if ( ! title ) {
200+ erroredFiles . push ( { filePath : file . path , error : `metadata field \'${ titleFieldName } \' not found or empty` } ) ;
201+ continue ;
202+ }
203+
204+ let results : MediaTypeModel [ ] = [ ] ;
205+ try {
206+ results = await this . apiManager . query ( title , selectedAPIs ) ;
207+ } catch ( e ) {
208+ erroredFiles . push ( { filePath : file . path , error : e . toString ( ) } ) ;
209+ continue ;
210+ }
211+ if ( ! results || results . length === 0 ) {
212+ erroredFiles . push ( { filePath : file . path , error : `no search results` } ) ;
213+ continue ;
214+ }
215+
216+ let selectedResults : MediaTypeModel [ ] = await new Promise ( ( resolve , reject ) => {
217+ const searchResultModal = new MediaDbSearchResultModal ( this . app , this , results , ( err , res ) => {
218+ if ( err ) return reject ( err ) ;
219+ resolve ( res ) ;
220+ } ) ;
221+ searchResultModal . title = `Results for \'${ title } \'` ;
222+ searchResultModal . open ( ) ;
223+ } ) ;
224+
225+ await this . createMediaDbNote ( async ( ) => selectedResults ) ;
226+ }
227+ }
228+
229+ if ( erroredFiles . length > 0 ) {
230+ const title = `bulk import error report ${ dateTimeToString ( new Date ( ) ) } ` ;
231+ const filePath = `${ this . settings . folder . replace ( / \/ $ / , '' ) } /${ title } .md` ;
232+
233+ const table = [ [ 'file' , 'error' ] ] . concat ( erroredFiles . map ( x => [ x . filePath , x . error ] ) ) ;
234+ // console.log(table)
235+ let fileContent = `# ${ title } \n\n${ markdownTable ( table ) } ` ;
236+
237+ const targetFile = await this . app . vault . create ( filePath , fileContent ) ;
238+ }
239+ }
240+
241+ async openMediaDbAdvancedSearchModal ( ) : Promise < MediaTypeModel [ ] > {
242+ return new Promise ( ( ( resolve , reject ) => {
243+ new MediaDbAdvancedSearchModal ( this . app , this , ( err , results ) => {
244+ if ( err ) {
245+ return reject ( err ) ;
246+ }
247+ new MediaDbSearchResultModal ( this . app , this , results , ( err2 , res ) => {
248+ if ( err2 ) {
249+ return reject ( err2 ) ;
250+ }
251+ resolve ( res ) ;
252+ } ) . open ( ) ;
253+ } ) . open ( ) ;
254+ } ) ) ;
255+ }
256+
257+ async openMediaDbIdSearchModal ( ) : Promise < MediaTypeModel > {
258+ return new Promise ( ( ( resolve , reject ) => {
259+ new MediaDbIdSearchModal ( this . app , this , ( err , res ) => {
260+ if ( err ) return reject ( err ) ;
261+ resolve ( res ) ;
262+ } ) . open ( ) ;
263+ } ) ) ;
264+ }
265+
181266 async loadSettings ( ) {
182267 this . settings = Object . assign ( { } , DEFAULT_SETTINGS , await this . loadData ( ) ) ;
183268 }
0 commit comments