1
- /**@type import('vscode') */
1
+ /**@type import('vscode') */ // soon: https://devblogs.microsoft.com/typescript/announcing-typescript-5-5-rc/#type-imports-in-jsdoc
2
2
const vscode = require ( "vscode" ) ;
3
3
const crypto = require ( 'crypto' ) ;
4
4
const https = require ( 'https' ) ;
@@ -21,19 +21,21 @@ const fetch = (url, opt, data) => new Promise((resolve, reject) => {
21
21
// - /track/:id gives contributors but /search/track?q= don't
22
22
// - inconsistent listing structure (/playlist/:id => tracks.data, sometimes=>data, sometimes data.tracks)
23
23
// browse can be called from: user query / self list(from static menu) / self list(from fetch result)
24
- async function browse ( url_or_event , label ) {
25
- console . log ( url_or_event ) ;
24
+ async function browse ( url_or_event_or_ids , label ) {
25
+ console . log ( url_or_event_or_ids ) ;
26
26
try {
27
- const url = typeof ( url_or_event ) == "string" ? url_or_event : '/' ;
27
+ if ( Array . isArray ( url_or_event_or_ids ) ) return url_or_event_or_ids . map ( id => ( { id } ) ) ;
28
+ const ignoreFocusOut = true ;
29
+ const url = typeof ( url_or_event_or_ids ) == "string" ? url_or_event_or_ids : '/' ;
28
30
const id = url . replace ( / \d + / g, '0' ) . replace ( / [ ^ \w ] / g, '_' ) ;
29
31
const menus = conf . get ( 'menus' ) ;
30
32
const title = ( label || '' ) . replace ( / \$ \( .+ ?\) / g, '' ) ;
31
33
if ( url . endsWith ( '=' ) || url . endsWith ( '/0' ) ) { // query step
32
- const input = await vscode . window . showInputBox ( { title } ) ;
34
+ const input = await vscode . window . showInputBox ( { title, ignoreFocusOut } ) ;
33
35
if ( ! input ) return ;
34
36
return await browse ( url . replace ( / 0 $ / , '' ) + input , `${ label } : ${ input } ` ) ;
35
37
} else if ( menus [ id ] ) { // menu step
36
- const pick = menus [ id ] . length > 1 ? await vscode . window . showQuickPick ( menus [ id ] , { title : title || url } ) : menus [ id ] [ 0 ] ;
38
+ const pick = menus [ id ] . length > 1 ? await vscode . window . showQuickPick ( menus [ id ] , { title : title || url , ignoreFocusOut } ) : menus [ id ] [ 0 ] ;
37
39
if ( ! pick ) return ;
38
40
return await browse ( url + pick . path , pick . label ) ;
39
41
} else { // fetch step
@@ -49,7 +51,7 @@ async function browse(url_or_event, label) {
49
51
description : [ entry . artist ?. name , entry . title_version , entry . nb_tracks ] . join ( ' ' ) ,
50
52
path : `/${ entry . type } /${ entry . id } ` ,
51
53
} ) ) ;
52
- const picks = await vscode . window . showQuickPick ( choices , { title : title || url , canPickMany } ) ;
54
+ const picks = await vscode . window . showQuickPick ( choices , { title : title || url , canPickMany, ignoreFocusOut } ) ;
53
55
if ( ! picks ) return ;
54
56
return canPickMany ? picks : await browse ( picks . path , picks . label ) ;
55
57
}
@@ -70,14 +72,17 @@ const with_url = async (songs) => songs?.length ? await vscode.window.withProgre
70
72
license_token : USR_NFO . USER . OPTIONS . license_token ,
71
73
media : [ { type : "FULL" , formats : [ { cipher : "BF_CBC_STRIPE" , format : "MP3_128" } ] } ]
72
74
} ) ) ) ) ;
75
+ console . log ( SNG_NFO )
73
76
const errors = URL_NFO . data . map ( ( nfo , i ) => [ nfo . errors , songs [ i ] ] ) . filter ( ( [ err ] ) => err ) . map ( ( [ [ err ] , sng ] ) => `${ sng . title } : ${ err . message } (${ err . code } )` ) . join ( '\n' ) ;
74
77
if ( errors ) setTimeout ( ( ) => vscode . window . showWarningMessage ( errors ) , 500 ) ; // can't warn while progress ?
75
78
return songs . map ( ( { /* api :*/ id, md5_image, duration, title_short, title_version, artist, contributors,
76
79
/*cache:*/ title, version, artists } , i ) => ( {
77
- id, md5_image, duration,
78
- title : title_short ?. replace ( / ? \( f e a t .* ?\) / , '' ) || title ,
80
+ id,
81
+ md5_image : md5_image || SNG_NFO . data [ i ] . ALB_PICTURE ,
82
+ duration : duration || + SNG_NFO . data [ i ] . DURATION ,
83
+ title : title || SNG_NFO . data [ i ] . SNG_TITLE . replace ( / ? \( f e a t .* ?\) / , '' ) ,
79
84
version : title_version || version ,
80
- artists : artists ?? ( contributors || [ artist ] ) ?. map ( ( { id , name } ) => ( { id, name } ) ) ,
85
+ artists : artists || SNG_NFO . data [ i ] . ARTISTS . map ( a => ( { id : a . ART_ID , name : a . ART_NAME , md5 : a . ART_PICTURE } ) ) ,
81
86
size : + SNG_NFO . data [ i ] . FILESIZE ,
82
87
expire : SNG_NFO . data [ i ] . TRACK_TOKEN_EXPIRE ,
83
88
url : URL_NFO . data [ i ] . media ?. [ 0 ] ?. sources ?. [ 0 ] ?. url
@@ -115,6 +120,7 @@ class DzrWebView { // can't Audio() in VSCode, we need a webview
115
120
this . initAckSemaphore ( ) ;
116
121
this . state . queue = conf . get ( 'queue' ) ; // first is best
117
122
this . state . looping = conf . get ( 'looping' ) ;
123
+ console . log ( vscode . ThemeIcon . File )
118
124
}
119
125
renderStatus ( ) {
120
126
const index = this . state . queue ?. indexOf ( this . state . current ) ;
@@ -159,13 +165,13 @@ class DzrWebView { // can't Audio() in VSCode, we need a webview
159
165
dragMimeTypes = [ 'text/uri-list' ] ;
160
166
_onDidChangeTreeData = new vscode . EventEmitter ( ) ;
161
167
onDidChangeTreeData = this . _onDidChangeTreeData . event ;
168
+ /**@type {import('vscode').TreeView }*/
162
169
treeView = vscode . window . createTreeView ( 'dzr.queue' , { treeDataProvider : this , dragAndDropController : this , canSelectMany : true } ) ;
163
-
164
170
/**@returns {vscode.TreeItem } */
165
171
getTreeItem = ( item ) => ( {
166
- iconPath : vscode . ThemeIcon . File ,
172
+ iconPath : new vscode . ThemeIcon ( "music" ) ,
167
173
label : item . title + ' - ' + item . artists . map ( a => a . name ) . join ( ) ,
168
- description : hhmmss ( item . duration ) + " " + ( item . version || '' ) ,
174
+ description : hhmmss ( item . duration || 0 ) + " " + ( item . version || '' ) ,
169
175
contextValue : 'dzr.track' ,
170
176
command : { title : 'Play' , command : 'dzr.load' , tooltip : 'Play' , arguments : [ this . state . queue . indexOf ( item ) ] } ,
171
177
//tooltip: JSON.stringify(item, null, 2),
@@ -182,7 +188,7 @@ class DzrWebView { // can't Audio() in VSCode, we need a webview
182
188
this . state . queue = [ ...striped . slice ( 0 , index ) , ...sources , ...striped . slice ( index ) ] ;
183
189
}
184
190
}
185
- exports . activate = async function ( /**@type {vscode.ExtensionContext }*/ context ) {
191
+ exports . activate = async function ( /**@type {import(' vscode') .ExtensionContext }*/ context ) {
186
192
// deezer didn't DMCA'd dzr so let's follow the same path here
187
193
conf . get ( 'cbc' ) || vscode . window . withProgress ( { title : 'Extracting CBC key...' , location } , async ( ) => {
188
194
const html_url = 'https://www.deezer.com/en/channels/explore' ;
@@ -199,18 +205,25 @@ exports.activate = async function (/**@type {vscode.ExtensionContext}*/ context)
199
205
const dzr = new DzrWebView ( ) ;
200
206
const htmlUri = vscode . Uri . joinPath ( context . extensionUri , 'webview.html' ) ;
201
207
const iconUri = vscode . Uri . joinPath ( context . extensionUri , 'logo.svg' ) ; //same for light+dark
208
+
202
209
context . subscriptions . push ( ...dzr . statuses , dzr . treeView ,
210
+ // catch vscode://yne.dzr/* urls
211
+ vscode . window . registerUriHandler ( { handleUri ( uri ) { ( ( { path, query } ) => vscode . commands . executeCommand ( `dzr.${ path . slice ( 1 ) } ` , ...( query ? JSON . parse ( query ) : [ ] ) ) ) ( vscode . Uri . parse ( uri ) ) ; } } ) ,
203
212
vscode . commands . registerCommand ( 'dzr.show' , ( ) => dzr . show ( htmlUri , iconUri ) ) ,
204
213
vscode . commands . registerCommand ( "dzr.play" , ( ) => dzr . post ( 'play' ) ) ,
205
214
vscode . commands . registerCommand ( "dzr.pause" , ( ) => dzr . post ( 'pause' ) ) ,
206
215
vscode . commands . registerCommand ( "dzr.href" , ( track ) => vscode . env . openExternal ( vscode . Uri . parse ( `https://deezer.com/track/${ track . id } ` ) ) ) ,
207
216
vscode . commands . registerCommand ( "dzr.loopQueue" , ( ) => dzr . state . looping = "queue" ) ,
208
217
vscode . commands . registerCommand ( "dzr.loopTrack" , ( ) => dzr . state . looping = "track" ) ,
209
218
vscode . commands . registerCommand ( "dzr.loopOff" , ( ) => dzr . state . looping = "off" ) ,
210
- vscode . commands . registerCommand ( "dzr.add" , async ( path , label ) => dzr . state . queue = [ ...dzr . state . queue , ...await with_url ( await browse ( path , label ) ) || [ ] ] ) ,
219
+ vscode . commands . registerCommand ( "dzr.add" , async ( path , label ) => with_url ( await browse ( path , label ) ) . then ( tracks => dzr . state . queue = [ ...dzr . state . queue , ...tracks ] ) ) ,
211
220
vscode . commands . registerCommand ( "dzr.remove" , async ( item , items ) => ( items || [ item ] ) . map ( i => vscode . commands . executeCommand ( 'dzr.removeAt' , dzr . state . queue . indexOf ( i ) ) ) ) ,
212
221
vscode . commands . registerCommand ( "dzr.removeAt" , async ( index ) => index >= 0 && ( dzr . state . queue = [ ...dzr . state . queue . slice ( 0 , index ) , ...dzr . state . queue . slice ( index + 1 ) ] ) ) ,
213
222
vscode . commands . registerCommand ( "dzr.clear" , async ( ) => dzr . state . queue = [ ] ) ,
223
+ vscode . commands . registerCommand ( "dzr.share" , async ( track , tracks ) => {
224
+ const ids = JSON . stringify ( track ? [ ( tracks || [ track ] ) . map ( e => e . id || track . id ) ] : [ dzr . state . queue . map ( q => q . id ) ] ) ;
225
+ vscode . env . clipboard . writeText ( new vscode . Uri ( "vscode" , context . extension . id , '/add' , ids ) . toString ( ) )
226
+ } ) ,
214
227
vscode . commands . registerCommand ( "dzr.shuffle" , async ( ) => {
215
228
const shuffle = [ ...dzr . state . queue ] ;
216
229
for ( let i = shuffle . length - 1 ; i > 0 ; i -- ) {
@@ -230,7 +243,7 @@ exports.activate = async function (/**@type {vscode.ExtensionContext}*/ context)
230
243
while ( ! dzr . state . ready ) await wait ( ) ;
231
244
}
232
245
dzr . state . current = dzr . state . queue [ pos ] ;
233
- if ( dzr . state . current . expire < ( new Date ( ) / 1000 ) ) {
246
+ if ( ( dzr . state . current . expire || 0 ) < ( new Date ( ) / 1000 ) ) {
234
247
dzr . state . queue = await with_url ( dzr . state . queue ) ; //TODO: hope item is now up to date
235
248
}
236
249
const hex = ( str ) => str . split ( '' ) . map ( c => c . charCodeAt ( 0 ) )
0 commit comments