@@ -234,6 +234,8 @@ fn get_setup() -> Setup {
234
234
const QUIET : & str = "quiet" ;
235
235
const SYSTEM_CACHE : & str = "system-cache" ;
236
236
const TEMP_DIR : & str = "tmp" ;
237
+ const TOKEN : & str = "token" ;
238
+ const TOKEN_PORT : & str = "token-port" ;
237
239
const USERNAME : & str = "username" ;
238
240
const VERBOSE : & str = "verbose" ;
239
241
const VERSION : & str = "version" ;
@@ -276,6 +278,8 @@ fn get_setup() -> Setup {
276
278
const ALSA_MIXER_INDEX_SHORT : & str = "s" ;
277
279
const ALSA_MIXER_CONTROL_SHORT : & str = "T" ;
278
280
const TEMP_DIR_SHORT : & str = "t" ;
281
+ const TOKEN_SHORT : & str = "k" ;
282
+ const TOKEN_PORT_SHORT : & str = "K" ;
279
283
const NORMALISATION_ATTACK_SHORT : & str = "U" ;
280
284
const USERNAME_SHORT : & str = "u" ;
281
285
const VERSION_SHORT : & str = "V" ;
@@ -415,6 +419,18 @@ fn get_setup() -> Setup {
415
419
DEVICE_IS_GROUP ,
416
420
"Whether the device represents a group. Defaults to false." ,
417
421
)
422
+ . optopt (
423
+ TOKEN_SHORT ,
424
+ TOKEN ,
425
+ "Spotify access token to sign in with. Use empty string to obtain token." ,
426
+ "TOKEN" ,
427
+ )
428
+ . optopt (
429
+ TOKEN_PORT_SHORT ,
430
+ TOKEN_PORT ,
431
+ "The port the oauth redirect server uses 1 - 65535. Ports <= 1024 may require root privileges." ,
432
+ "PORT" ,
433
+ )
418
434
. optopt (
419
435
TEMP_DIR_SHORT ,
420
436
TEMP_DIR ,
@@ -670,7 +686,10 @@ fn get_setup() -> Setup {
670
686
trace ! ( "Environment variable(s):" ) ;
671
687
672
688
for ( k, v) in & env_vars {
673
- if matches ! ( k. as_str( ) , "LIBRESPOT_PASSWORD" | "LIBRESPOT_USERNAME" ) {
689
+ if matches ! (
690
+ k. as_str( ) ,
691
+ "LIBRESPOT_PASSWORD" | "LIBRESPOT_USERNAME" | "LIBRESPOT_TOKEN"
692
+ ) {
674
693
trace ! ( "\t \t {k}=\" XXXXXXXX\" " ) ;
675
694
} else if v. is_empty ( ) {
676
695
trace ! ( "\t \t {k}=" ) ;
@@ -702,7 +721,10 @@ fn get_setup() -> Setup {
702
721
&& matches. opt_defined ( opt)
703
722
&& matches. opt_present ( opt)
704
723
{
705
- if matches ! ( opt, PASSWORD | PASSWORD_SHORT | USERNAME | USERNAME_SHORT ) {
724
+ if matches ! (
725
+ opt,
726
+ PASSWORD | PASSWORD_SHORT | USERNAME | USERNAME_SHORT | TOKEN | TOKEN_SHORT
727
+ ) {
706
728
// Don't log creds.
707
729
trace ! ( "\t \t {opt} \" XXXXXXXX\" " ) ;
708
730
} else {
@@ -1081,129 +1103,6 @@ fn get_setup() -> Setup {
1081
1103
}
1082
1104
} ;
1083
1105
1084
- let credentials = {
1085
- let cached_creds = cache. as_ref ( ) . and_then ( Cache :: credentials) ;
1086
-
1087
- if let Some ( username) = opt_str ( USERNAME ) {
1088
- if username. is_empty ( ) {
1089
- empty_string_error_msg ( USERNAME , USERNAME_SHORT ) ;
1090
- }
1091
- if let Some ( password) = opt_str ( PASSWORD ) {
1092
- if password. is_empty ( ) {
1093
- empty_string_error_msg ( PASSWORD , PASSWORD_SHORT ) ;
1094
- }
1095
- Some ( Credentials :: with_password ( username, password) )
1096
- } else {
1097
- match cached_creds {
1098
- Some ( creds) if Some ( & username) == creds. username . as_ref ( ) => Some ( creds) ,
1099
- _ => {
1100
- let prompt = & format ! ( "Password for {username}: " ) ;
1101
- match rpassword:: prompt_password ( prompt) {
1102
- Ok ( password) => {
1103
- if !password. is_empty ( ) {
1104
- Some ( Credentials :: with_password ( username, password) )
1105
- } else {
1106
- trace ! ( "Password was empty." ) ;
1107
- if cached_creds. is_some ( ) {
1108
- trace ! ( "Using cached credentials." ) ;
1109
- }
1110
- cached_creds
1111
- }
1112
- }
1113
- Err ( e) => {
1114
- warn ! ( "Cannot parse password: {}" , e) ;
1115
- if cached_creds. is_some ( ) {
1116
- trace ! ( "Using cached credentials." ) ;
1117
- }
1118
- cached_creds
1119
- }
1120
- }
1121
- }
1122
- }
1123
- }
1124
- } else {
1125
- if cached_creds. is_some ( ) {
1126
- trace ! ( "Using cached credentials." ) ;
1127
- }
1128
- cached_creds
1129
- }
1130
- } ;
1131
-
1132
- let enable_discovery = !opt_present ( DISABLE_DISCOVERY ) ;
1133
-
1134
- if credentials. is_none ( ) && !enable_discovery {
1135
- error ! ( "Credentials are required if discovery is disabled." ) ;
1136
- exit ( 1 ) ;
1137
- }
1138
-
1139
- if !enable_discovery && opt_present ( ZEROCONF_PORT ) {
1140
- warn ! (
1141
- "With the `--{}` / `-{}` flag set `--{}` / `-{}` has no effect." ,
1142
- DISABLE_DISCOVERY , DISABLE_DISCOVERY_SHORT , ZEROCONF_PORT , ZEROCONF_PORT_SHORT
1143
- ) ;
1144
- }
1145
-
1146
- let zeroconf_port = if enable_discovery {
1147
- opt_str ( ZEROCONF_PORT )
1148
- . map ( |port| match port. parse :: < u16 > ( ) {
1149
- Ok ( value) if value != 0 => value,
1150
- _ => {
1151
- let valid_values = & format ! ( "1 - {}" , u16 :: MAX ) ;
1152
- invalid_error_msg ( ZEROCONF_PORT , ZEROCONF_PORT_SHORT , & port, valid_values, "" ) ;
1153
-
1154
- exit ( 1 ) ;
1155
- }
1156
- } )
1157
- . unwrap_or ( 0 )
1158
- } else {
1159
- 0
1160
- } ;
1161
-
1162
- // #1046: not all connections are supplied an `autoplay` user attribute to run statelessly.
1163
- // This knob allows for a manual override.
1164
- let autoplay = match opt_str ( AUTOPLAY ) {
1165
- Some ( value) => match value. as_ref ( ) {
1166
- "on" => Some ( true ) ,
1167
- "off" => Some ( false ) ,
1168
- _ => {
1169
- invalid_error_msg (
1170
- AUTOPLAY ,
1171
- AUTOPLAY_SHORT ,
1172
- & opt_str ( AUTOPLAY ) . unwrap_or_default ( ) ,
1173
- "on, off" ,
1174
- "" ,
1175
- ) ;
1176
- exit ( 1 ) ;
1177
- }
1178
- } ,
1179
- None => SessionConfig :: default ( ) . autoplay ,
1180
- } ;
1181
-
1182
- let zeroconf_ip: Vec < std:: net:: IpAddr > = if opt_present ( ZEROCONF_INTERFACE ) {
1183
- if let Some ( zeroconf_ip) = opt_str ( ZEROCONF_INTERFACE ) {
1184
- zeroconf_ip
1185
- . split ( ',' )
1186
- . map ( |s| {
1187
- s. trim ( ) . parse :: < std:: net:: IpAddr > ( ) . unwrap_or_else ( |_| {
1188
- invalid_error_msg (
1189
- ZEROCONF_INTERFACE ,
1190
- ZEROCONF_INTERFACE_SHORT ,
1191
- s,
1192
- "IPv4 and IPv6 addresses" ,
1193
- "" ,
1194
- ) ;
1195
- exit ( 1 ) ;
1196
- } )
1197
- } )
1198
- . collect ( )
1199
- } else {
1200
- warn ! ( "Unable to use zeroconf-interface option, default to all interfaces." ) ;
1201
- vec ! [ ]
1202
- }
1203
- } else {
1204
- vec ! [ ]
1205
- } ;
1206
-
1207
1106
let connect_config = {
1208
1107
let connect_default_config = ConnectConfig :: default ( ) ;
1209
1108
@@ -1330,6 +1229,26 @@ fn get_setup() -> Setup {
1330
1229
}
1331
1230
} ;
1332
1231
1232
+ // #1046: not all connections are supplied an `autoplay` user attribute to run statelessly.
1233
+ // This knob allows for a manual override.
1234
+ let autoplay = match opt_str ( AUTOPLAY ) {
1235
+ Some ( value) => match value. as_ref ( ) {
1236
+ "on" => Some ( true ) ,
1237
+ "off" => Some ( false ) ,
1238
+ _ => {
1239
+ invalid_error_msg (
1240
+ AUTOPLAY ,
1241
+ AUTOPLAY_SHORT ,
1242
+ & opt_str ( AUTOPLAY ) . unwrap_or_default ( ) ,
1243
+ "on, off" ,
1244
+ "" ,
1245
+ ) ;
1246
+ exit ( 1 ) ;
1247
+ }
1248
+ } ,
1249
+ None => SessionConfig :: default ( ) . autoplay ,
1250
+ } ;
1251
+
1333
1252
let session_config = SessionConfig {
1334
1253
device_id : device_id ( & connect_config. name ) ,
1335
1254
proxy : opt_str ( PROXY ) . or_else ( || std:: env:: var ( "http_proxy" ) . ok ( ) ) . map (
@@ -1364,6 +1283,130 @@ fn get_setup() -> Setup {
1364
1283
..SessionConfig :: default ( )
1365
1284
} ;
1366
1285
1286
+ let credentials = {
1287
+ let cached_creds = cache. as_ref ( ) . and_then ( Cache :: credentials) ;
1288
+
1289
+ let token_port = if opt_present ( TOKEN_PORT ) {
1290
+ opt_str ( TOKEN_PORT )
1291
+ . map ( |port| match port. parse :: < u16 > ( ) {
1292
+ Ok ( value) => value,
1293
+ _ => {
1294
+ let valid_values = & format ! ( "1 - {}" , u16 :: MAX ) ;
1295
+ invalid_error_msg ( TOKEN_PORT , TOKEN_PORT_SHORT , & port, valid_values, "" ) ;
1296
+
1297
+ exit ( 1 ) ;
1298
+ }
1299
+ } )
1300
+ . unwrap_or ( 0 )
1301
+ } else {
1302
+ 5588
1303
+ } ;
1304
+ if let Some ( mut access_token) = opt_str ( TOKEN ) {
1305
+ if access_token. is_empty ( ) {
1306
+ access_token =
1307
+ librespot:: oauth:: get_access_token ( & session_config. client_id , token_port) ;
1308
+ }
1309
+ Some ( Credentials :: with_access_token ( access_token) )
1310
+ } else if let Some ( username) = opt_str ( USERNAME ) {
1311
+ if username. is_empty ( ) {
1312
+ empty_string_error_msg ( USERNAME , USERNAME_SHORT ) ;
1313
+ }
1314
+ if let Some ( password) = opt_str ( PASSWORD ) {
1315
+ if password. is_empty ( ) {
1316
+ empty_string_error_msg ( PASSWORD , PASSWORD_SHORT ) ;
1317
+ }
1318
+ Some ( Credentials :: with_password ( username, password) )
1319
+ } else {
1320
+ match cached_creds {
1321
+ Some ( creds) if Some ( & username) == creds. username . as_ref ( ) => Some ( creds) ,
1322
+ _ => {
1323
+ let prompt = & format ! ( "Password for {username}: " ) ;
1324
+ match rpassword:: prompt_password ( prompt) {
1325
+ Ok ( password) => {
1326
+ if !password. is_empty ( ) {
1327
+ Some ( Credentials :: with_password ( username, password) )
1328
+ } else {
1329
+ trace ! ( "Password was empty." ) ;
1330
+ if cached_creds. is_some ( ) {
1331
+ trace ! ( "Using cached credentials." ) ;
1332
+ }
1333
+ cached_creds
1334
+ }
1335
+ }
1336
+ Err ( e) => {
1337
+ warn ! ( "Cannot parse password: {}" , e) ;
1338
+ if cached_creds. is_some ( ) {
1339
+ trace ! ( "Using cached credentials." ) ;
1340
+ }
1341
+ cached_creds
1342
+ }
1343
+ }
1344
+ }
1345
+ }
1346
+ }
1347
+ } else {
1348
+ if cached_creds. is_some ( ) {
1349
+ trace ! ( "Using cached credentials." ) ;
1350
+ }
1351
+ cached_creds
1352
+ }
1353
+ } ;
1354
+
1355
+ let enable_discovery = !opt_present ( DISABLE_DISCOVERY ) ;
1356
+
1357
+ if credentials. is_none ( ) && !enable_discovery {
1358
+ error ! ( "Credentials are required if discovery is disabled." ) ;
1359
+ exit ( 1 ) ;
1360
+ }
1361
+
1362
+ if !enable_discovery && opt_present ( ZEROCONF_PORT ) {
1363
+ warn ! (
1364
+ "With the `--{}` / `-{}` flag set `--{}` / `-{}` has no effect." ,
1365
+ DISABLE_DISCOVERY , DISABLE_DISCOVERY_SHORT , ZEROCONF_PORT , ZEROCONF_PORT_SHORT
1366
+ ) ;
1367
+ }
1368
+
1369
+ let zeroconf_port = if enable_discovery {
1370
+ opt_str ( ZEROCONF_PORT )
1371
+ . map ( |port| match port. parse :: < u16 > ( ) {
1372
+ Ok ( value) if value != 0 => value,
1373
+ _ => {
1374
+ let valid_values = & format ! ( "1 - {}" , u16 :: MAX ) ;
1375
+ invalid_error_msg ( ZEROCONF_PORT , ZEROCONF_PORT_SHORT , & port, valid_values, "" ) ;
1376
+
1377
+ exit ( 1 ) ;
1378
+ }
1379
+ } )
1380
+ . unwrap_or ( 0 )
1381
+ } else {
1382
+ 0
1383
+ } ;
1384
+
1385
+ let zeroconf_ip: Vec < std:: net:: IpAddr > = if opt_present ( ZEROCONF_INTERFACE ) {
1386
+ if let Some ( zeroconf_ip) = opt_str ( ZEROCONF_INTERFACE ) {
1387
+ zeroconf_ip
1388
+ . split ( ',' )
1389
+ . map ( |s| {
1390
+ s. trim ( ) . parse :: < std:: net:: IpAddr > ( ) . unwrap_or_else ( |_| {
1391
+ invalid_error_msg (
1392
+ ZEROCONF_INTERFACE ,
1393
+ ZEROCONF_INTERFACE_SHORT ,
1394
+ s,
1395
+ "IPv4 and IPv6 addresses" ,
1396
+ "" ,
1397
+ ) ;
1398
+ exit ( 1 ) ;
1399
+ } )
1400
+ } )
1401
+ . collect ( )
1402
+ } else {
1403
+ warn ! ( "Unable to use zeroconf-interface option, default to all interfaces." ) ;
1404
+ vec ! [ ]
1405
+ }
1406
+ } else {
1407
+ vec ! [ ]
1408
+ } ;
1409
+
1367
1410
let player_config = {
1368
1411
let player_default_config = PlayerConfig :: default ( ) ;
1369
1412
0 commit comments