1 /** 2 * \file sendtr.c 3 * Example program to send a music track to a device. 4 * This program is derived from the exact equivalent in libnjb. 5 * based on Enrique Jorreto Ledesma's work on the original program by 6 * Shaun Jackman and Linus Walleij. 7 * 8 * Copyright (C) 2003-2010 Linus Walleij <triad (at) df.lth.se> 9 * Copyright (C) 2003-2005 Shaun Jackman 10 * Copyright (C) 2003-2005 Enrique Jorrete Ledesma 11 * Copyright (C) 2006 Chris A. Debenham <chris (at) adebenham.com> 12 * Copyright (C) 2008 Nicolas Pennequin <nicolas.pennequin (at) free.fr> 13 * Copyright (C) 2008 Joseph Nahmias <joe (at) nahmias.net> 14 * 15 * This library is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU Lesser General Public 17 * License as published by the Free Software Foundation; either 18 * version 2 of the License, or (at your option) any later version. 19 * 20 * This library is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 * Lesser General Public License for more details. 24 * 25 * You should have received a copy of the GNU Lesser General Public 26 * License along with this library; if not, write to the 27 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 28 * Boston, MA 02111-1307, USA. 29 */ 30 31 #include <stdlib.h> 32 #include <limits.h> 33 #include <string.h> 34 #include <libgen.h> 35 #include <sys/stat.h> 36 #include <sys/types.h> 37 #include <fcntl.h> 38 #ifdef HAVE_LANGINFO_H 39 #include <langinfo.h> 40 #endif 41 42 #include "config.h" 43 #include "common.h" 44 #include "util.h" 45 #include "connect.h" 46 #include "libmtp.h" 47 #include "pathutils.h" 48 49 extern LIBMTP_folder_t *folders; 50 extern LIBMTP_file_t *files; 51 extern LIBMTP_mtpdevice_t *device; 52 53 void sendtrack_usage (void) 54 { 55 fprintf(stderr, "usage: sendtr [ -D debuglvl ] [ -q ]\n"); 56 fprintf(stderr, "-t <title> -a <artist> -A <Album artist> -w <writer or composer>\n"); 57 fprintf(stderr, " -l <album> -c <codec> -g <genre> -n <track number> -y <year>\n"); 58 fprintf(stderr, " -d <duration in seconds> -s <storage_id> <local path> <remote path>\n"); 59 fprintf(stderr, "(-q means the program will not ask for missing information.)\n"); 60 } 61 62 static char *prompt (const char *prompt, char *buffer, size_t bufsz, int required) 63 { 64 char *cp, *bp; 65 66 while (1) { 67 fprintf(stdout, "%s> ", prompt); 68 if ( fgets(buffer, bufsz, stdin) == NULL ) { 69 if (ferror(stdin)) { 70 perror("fgets"); 71 } else { 72 fprintf(stderr, "EOF on stdin\n"); 73 } 74 return NULL; 75 } 76 77 cp = strrchr(buffer, '\n'); 78 if ( cp != NULL ) *cp = '\0'; 79 80 bp = buffer; 81 while ( bp != cp ) { 82 if ( *bp != ' ' && *bp != '\t' ) return bp; 83 bp++; 84 } 85 86 if (! required) return bp; 87 } 88 } 89 90 static int add_track_to_album(LIBMTP_album_t *albuminfo, LIBMTP_track_t *trackmeta) 91 { 92 LIBMTP_album_t *album; 93 LIBMTP_album_t *album_orig; 94 LIBMTP_album_t *found_album = NULL; 95 int ret; 96 97 /* Look for the album */ 98 album = LIBMTP_Get_Album_List(device); 99 album_orig = album; 100 while(album != NULL) { 101 if ((album->name != NULL && 102 album->artist != NULL && 103 !strcmp(album->name, albuminfo->name) && 104 !strcmp(album->artist, albuminfo->artist)) || 105 (album->name != NULL && 106 album->composer != NULL && 107 !strcmp(album->name, albuminfo->name) && 108 !strcmp(album->composer, albuminfo->composer))) { 109 /* Disconnect this album for later use */ 110 found_album = album; 111 album = album->next; 112 found_album->next = NULL; 113 } else { 114 album = album->next; 115 } 116 } 117 118 if (found_album == NULL) { 119 printf("Could not find Album. Retrying with only Album name\n"); 120 album = album_orig; 121 while(album != NULL) { 122 if ((album->name != NULL) && 123 !strcmp(album->name, albuminfo->name) ){ 124 /* Disconnect this album for later use */ 125 found_album = album; 126 album = album->next; 127 found_album->next = NULL; 128 } else { 129 album = album->next; 130 } 131 } 132 } 133 134 if (found_album != NULL) { 135 uint32_t *tracks; 136 137 tracks = (uint32_t *)malloc((found_album->no_tracks+1) * sizeof(uint32_t)); 138 printf("Album \"%s\" found: updating...\n", found_album->name); 139 if (!tracks) { 140 printf("failed malloc in add_track_to_album()\n"); 141 return 1; 142 } 143 found_album->no_tracks++; 144 if (found_album->tracks != NULL) { 145 memcpy(tracks, found_album->tracks, found_album->no_tracks * sizeof(uint32_t)); 146 free(found_album->tracks); 147 } 148 tracks[found_album->no_tracks-1] = trackmeta->item_id; 149 found_album->tracks = tracks; 150 ret = LIBMTP_Update_Album(device, found_album); 151 } else { 152 uint32_t *trackid; 153 154 trackid = (uint32_t *)malloc(sizeof(uint32_t)); 155 *trackid = trackmeta->item_id; 156 albuminfo->tracks = trackid; 157 albuminfo->no_tracks = 1; 158 albuminfo->storage_id = trackmeta->storage_id; 159 printf("Album doesn't exist: creating...\n"); 160 ret = LIBMTP_Create_New_Album(device, albuminfo); 161 /* albuminfo will be destroyed later by caller */ 162 } 163 164 /* Delete the earlier retrieved Album list */ 165 album=album_orig; 166 while(album!=NULL){ 167 LIBMTP_album_t *tmp; 168 169 tmp = album; 170 album = album->next; 171 LIBMTP_destroy_album_t(tmp); 172 } 173 174 if (ret != 0) { 175 printf("Error creating or updating album.\n"); 176 printf("(This could be due to that your device does not support albums.)\n"); 177 LIBMTP_Dump_Errorstack(device); 178 LIBMTP_Clear_Errorstack(device); 179 } else { 180 printf("success!\n"); 181 } 182 return ret; 183 } 184 185 int sendtrack_function(char * from_path, char * to_path, char *partist, char *palbumartist, char *ptitle, char *pgenre, char *palbum, char *pcomposer, uint16_t tracknum, uint16_t length, uint16_t year, uint32_t storageid, uint16_t quiet) 186 { 187 char *filename, *parent; 188 char artist[80], albumartist[80], title[80], genre[80], album[80], composer[80]; 189 char *to_path_copy = NULL; 190 char num[80]; 191 uint64_t filesize; 192 uint32_t parent_id = 0; 193 struct stat sb; 194 LIBMTP_track_t *trackmeta; 195 LIBMTP_album_t *albuminfo; 196 int ret; 197 198 printf("Sending track %s to %s\n", from_path, to_path); 199 200 to_path_copy = strdup(to_path); 201 parent = dirname(to_path_copy); 202 parent_id = parse_path (parent,files,folders); 203 if (parent_id == -1) { 204 free (to_path_copy); 205 printf("Parent folder could not be found, skipping\n"); 206 return 1; 207 } 208 strcpy (to_path_copy,to_path); 209 filename = basename(to_path_copy); 210 211 if (stat(from_path, &sb) == -1) { 212 fprintf(stderr, "%s: ", from_path); 213 perror("stat"); 214 free (to_path_copy); 215 return 1; 216 } 217 218 if (!S_ISREG(sb.st_mode)) { 219 free (to_path_copy); 220 return 0; 221 } 222 223 filesize = sb.st_size; 224 225 trackmeta = LIBMTP_new_track_t(); 226 trackmeta->filetype = find_filetype (from_path); 227 if (!LIBMTP_FILETYPE_IS_TRACK(trackmeta->filetype)) { 228 printf("Not a valid track codec: \"%s\"\n", LIBMTP_Get_Filetype_Description(trackmeta->filetype)); 229 LIBMTP_destroy_track_t(trackmeta); 230 free (to_path_copy); 231 return 1; 232 } 233 234 if ((ptitle == NULL) && (quiet == 0)) { 235 if ( (ptitle = prompt("Title", title, 80, 0)) != NULL ) 236 if (!strlen(ptitle)) ptitle = NULL; 237 } 238 239 if ((palbum == NULL) && (quiet == 0)) { 240 if ( (palbum = prompt("Album", album, 80, 0)) != NULL ) 241 if (!strlen(palbum)) palbum = NULL; 242 } 243 244 if ((palbumartist == NULL) && (quiet == 0)) { 245 if ( (palbumartist = prompt("Album artist", albumartist, 80, 0)) != NULL ) 246 if (!strlen(palbumartist)) palbumartist = NULL; 247 } 248 249 if ((partist == NULL) && (quiet == 0)) { 250 if ( (partist = prompt("Artist", artist, 80, 0)) != NULL ) 251 if (!strlen(partist)) partist = NULL; 252 } 253 254 if ((pcomposer == NULL) && (quiet == 0)) { 255 if ( (pcomposer = prompt("Writer or Composer", composer, 80, 0)) != NULL ) 256 if (!strlen(pcomposer)) pcomposer = NULL; 257 } 258 259 if ((pgenre == NULL) && (quiet == 0)) { 260 if ( (pgenre = prompt("Genre", genre, 80, 0)) != NULL ) 261 if (!strlen(pgenre)) pgenre = NULL; 262 } 263 264 if ((tracknum == 0) && (quiet == 0)) { 265 char *pnum; 266 if ( (pnum = prompt("Track number", num, 80, 0)) == NULL ) 267 tracknum = 0; 268 else 269 tracknum = strtoul(pnum, 0, 10); 270 } 271 272 if ((year == 0) && (quiet == 0)) { 273 char *pnum; 274 if ( (pnum = prompt("Year", num, 80, 0)) == NULL ) 275 year = 0; 276 else 277 year = strtoul(pnum, 0, 10); 278 } 279 280 if ((length == 0) && (quiet == 0)) { 281 char *pnum; 282 if ( (pnum = prompt("Length", num, 80, 0)) == NULL ) 283 length = 0; 284 else 285 length = strtoul(pnum, 0, 10); 286 } 287 288 printf("Sending track:\n"); 289 printf("Codec: %s\n", LIBMTP_Get_Filetype_Description(trackmeta->filetype)); 290 if (ptitle) { 291 printf("Title: %s\n", ptitle); 292 trackmeta->title = strdup(ptitle); 293 } 294 295 albuminfo = LIBMTP_new_album_t(); 296 297 if (palbum) { 298 printf("Album: %s\n", palbum); 299 trackmeta->album = strdup(palbum); 300 albuminfo->name = strdup(palbum); 301 } 302 if (palbumartist) { 303 printf("Album artist: %s\n", palbumartist); 304 albuminfo->artist = strdup(palbumartist); 305 } 306 if (partist) { 307 printf("Artist: %s\n", partist); 308 trackmeta->artist = strdup(partist); 309 if (palbumartist == NULL) 310 albuminfo->artist = strdup(partist); 311 } 312 if (pcomposer) { 313 printf("Writer or Composer: %s\n", pcomposer); 314 trackmeta->composer = strdup(pcomposer); 315 albuminfo->composer = strdup(pcomposer); 316 } 317 if (pgenre) { 318 printf("Genre: %s\n", pgenre); 319 trackmeta->genre = strdup(pgenre); 320 albuminfo->genre = strdup(pgenre); 321 } 322 if (year > 0) { 323 char tmp[80]; 324 printf("Year: %d\n", year); 325 snprintf(tmp, sizeof(tmp)-1, "%4d0101T0000.0", year); 326 tmp[sizeof(tmp)-1] = '\0'; 327 trackmeta->date = strdup(tmp); 328 } 329 if (tracknum > 0) { 330 printf("Track no: %d\n", tracknum); 331 trackmeta->tracknumber = tracknum; 332 } 333 if (length > 0) { 334 printf("Length: %d\n", length); 335 // Multiply by 1000 since this is in milliseconds 336 trackmeta->duration = length * 1000; 337 } 338 // We should always have this 339 if (filename != NULL) { 340 trackmeta->filename = strdup(filename); 341 } 342 trackmeta->filesize = filesize; 343 trackmeta->parent_id = parent_id; 344 { 345 int rc; 346 char *desc = NULL; 347 LIBMTP_devicestorage_t *pds = NULL; 348 349 if (0 != (rc=LIBMTP_Get_Storage(device, LIBMTP_STORAGE_SORTBY_NOTSORTED))) { 350 perror("LIBMTP_Get_Storage()"); 351 exit(-1); 352 } 353 for (pds = device->storage; pds != NULL; pds = pds->next) { 354 if (pds->id == storageid) { 355 desc = strdup(pds->StorageDescription); 356 break; 357 } 358 } 359 if (NULL != desc) { 360 printf("Storage ID: %s (%u)\n", desc, storageid); 361 free(desc); 362 } else 363 printf("Storage ID: %u\n", storageid); 364 trackmeta->storage_id = storageid; 365 } 366 367 printf("Sending track...\n"); 368 ret = LIBMTP_Send_Track_From_File(device, from_path, trackmeta, progress, NULL); 369 printf("\n"); 370 if (ret != 0) { 371 printf("Error sending track.\n"); 372 LIBMTP_Dump_Errorstack(device); 373 LIBMTP_Clear_Errorstack(device); 374 ret = 1; 375 } else { 376 printf("New track ID: %d\n", trackmeta->item_id); 377 } 378 379 /* Add here add to album call */ 380 if (palbum) 381 ret = add_track_to_album(albuminfo, trackmeta); 382 383 LIBMTP_destroy_album_t(albuminfo); 384 LIBMTP_destroy_track_t(trackmeta); 385 free (to_path_copy); 386 387 return ret; 388 } 389 390 int sendtrack_command (int argc, char **argv) { 391 int opt, ret; 392 extern int optind; 393 extern char *optarg; 394 char *partist = NULL; 395 char *palbumartist = NULL; 396 char *pcomposer = NULL; 397 char *ptitle = NULL; 398 char *pgenre = NULL; 399 char *pcodec = NULL; 400 char *palbum = NULL; 401 uint16_t tracknum = 0; 402 uint16_t length = 0; 403 uint16_t year = 0; 404 uint16_t quiet = 0; 405 uint32_t storageid = 0; 406 while ( (opt = getopt(argc, argv, "qD:t:a:A:w:l:c:g:n:d:y:s:")) != -1 ) { 407 switch (opt) { 408 case 't': 409 free (ptitle); 410 ptitle = strdup(optarg); 411 break; 412 case 'a': 413 free (partist); 414 partist = strdup(optarg); 415 break; 416 case 'A': 417 free (palbumartist); 418 palbumartist = strdup(optarg); 419 break; 420 case 'w': 421 free (pcomposer); 422 pcomposer = strdup(optarg); 423 break; 424 case 'l': 425 free (palbum); 426 palbum = strdup(optarg); 427 break; 428 case 'c': 429 free (pcodec); 430 pcodec = strdup(optarg); // FIXME: DSM check for MP3, WAV or WMA 431 break; 432 case 'g': 433 free (pgenre); 434 pgenre = strdup(optarg); 435 break; 436 case 'n': 437 tracknum = atoi(optarg); 438 break; 439 case 's': 440 storageid = (uint32_t) strtoul(optarg, NULL, 0); 441 break; 442 case 'd': 443 length = atoi(optarg); 444 break; 445 case 'y': 446 year = atoi(optarg); 447 break; 448 case 'q': 449 quiet = 1; 450 break; 451 default: 452 sendtrack_usage(); 453 } 454 } 455 argc -= optind; 456 argv += optind; 457 458 if ( argc != 2 ) { 459 printf("You need to pass a filename and destination.\n"); 460 sendtrack_usage(); 461 ret = 0; 462 } else { 463 checklang(); 464 printf("%s,%s,%s,%s,%s,%s,%s,%s,%d%d,%d,%u,%d\n",argv[0],argv[1],partist,palbumartist,ptitle,pgenre,palbum,pcomposer,tracknum, length, year, storageid, quiet); 465 ret = sendtrack_function(argv[0],argv[1],partist,palbumartist,ptitle,pgenre,palbum,pcomposer, tracknum, length, year, storageid, quiet); 466 } 467 free (ptitle); 468 free (partist); 469 free (palbumartist); 470 free (pcomposer); 471 free (palbum); 472 free (pcodec); 473 free (pgenre); 474 return ret; 475 } 476