BVB Source Codes

cmus Show cmus.c Source code

Return Download cmus: download cmus.c Source code - Download cmus Source code - Type:.c
  1. /*
  2.  * Copyright 2008-2013 Various Authors
  3.  * Copyright 2004 Timo Hirvonen
  4.  *
  5.  * This program is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU General Public License as
  7.  * published by the Free Software Foundation; either version 2 of the
  8.  * License, or (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful, but
  11.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  * General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. #include "cmus.h"
  20. #include "job.h"
  21. #include "lib.h"
  22. #include "pl.h"
  23. #include "player.h"
  24. #include "input.h"
  25. #include "play_queue.h"
  26. #include "cache.h"
  27. #include "misc.h"
  28. #include "file.h"
  29. #include "utils.h"
  30. #include "path.h"
  31. #include "options.h"
  32. #include "xmalloc.h"
  33. #include "debug.h"
  34. #include "load_dir.h"
  35. #include "ui_curses.h"
  36. #include "cache.h"
  37. #include "gbuf.h"
  38. #include "discid.h"
  39. #include "locking.h"
  40.  
  41. #include <sys/types.h>
  42. #include <sys/stat.h>
  43. #include <fcntl.h>
  44. #include <unistd.h>
  45. #include <dirent.h>
  46. #include <stdlib.h>
  47. #include <ctype.h>
  48. #include <strings.h>
  49.  
  50. /* save_playlist_cb, save_ext_playlist_cb */
  51. typedef int (*save_tracks_cb)(void *data, struct track_info *ti);
  52.  
  53. static char **playable_exts;
  54. static const char * const playlist_exts[] = { "m3u", "pl", "pls", NULL };
  55.  
  56. int cmus_next_track_request_fd;
  57. static int cmus_next_track_request_fd_priv;
  58. static pthread_mutex_t cmus_next_file_mutex = CMUS_MUTEX_INITIALIZER;
  59. static pthread_cond_t cmus_next_file_cond = CMUS_COND_INITIALIZER;
  60. static int cmus_next_file_provided;
  61. static struct track_info *cmus_next_file;
  62.  
  63. int cmus_init(void)
  64. {
  65.         playable_exts = ip_get_supported_extensions();
  66.         cache_init();
  67.         job_init();
  68.         play_queue_init();
  69.         return 0;
  70. }
  71.  
  72. void cmus_exit(void)
  73. {
  74.         job_exit();
  75.         if (cache_close())
  76.                 d_print("error: %s\n", strerror(errno));
  77. }
  78.  
  79. void cmus_next(void)
  80. {
  81.         struct track_info *info = cmus_get_next_track();
  82.         if (info)
  83.                 player_set_file(info);
  84. }
  85.  
  86. void cmus_prev(void)
  87. {
  88.         struct track_info *info;
  89.  
  90.         if (play_library) {
  91.                 info = lib_goto_prev();
  92.         } else {
  93.                 info = pl_goto_prev();
  94.         }
  95.  
  96.         if (info)
  97.                 player_set_file(info);
  98. }
  99.  
  100. void cmus_play_file(const char *filename)
  101. {
  102.         struct track_info *ti;
  103.  
  104.         cache_lock();
  105.         ti = cache_get_ti(filename, 0);
  106.         cache_unlock();
  107.         if (!ti) {
  108.                 error_msg("Couldn't get file information for %s\n", filename);
  109.                 return;
  110.         }
  111.  
  112.         player_play_file(ti);
  113. }
  114.  
  115. enum file_type cmus_detect_ft(const char *name, char **ret)
  116. {
  117.         char *absolute;
  118.         struct stat st;
  119.  
  120.         if (is_http_url(name) || is_cue_url(name)) {
  121.                 *ret = xstrdup(name);
  122.                 return FILE_TYPE_URL;
  123.         }
  124.  
  125.         if (is_cdda_url(name)) {
  126.                 *ret = complete_cdda_url(cdda_device, name);
  127.                 return FILE_TYPE_CDDA;
  128.         }
  129.  
  130.         *ret = NULL;
  131.         absolute = path_absolute(name);
  132.         if (absolute == NULL)
  133.                 return FILE_TYPE_INVALID;
  134.  
  135.         /* stat follows symlinks, lstat does not */
  136.         if (stat(absolute, &st) == -1) {
  137.                 free(absolute);
  138.                 return FILE_TYPE_INVALID;
  139.         }
  140.  
  141.         if (S_ISDIR(st.st_mode)) {
  142.                 *ret = absolute;
  143.                 return FILE_TYPE_DIR;
  144.         }
  145.         if (!S_ISREG(st.st_mode)) {
  146.                 free(absolute);
  147.                 errno = EINVAL;
  148.                 return FILE_TYPE_INVALID;
  149.         }
  150.  
  151.         *ret = absolute;
  152.         if (cmus_is_playlist(absolute))
  153.                 return FILE_TYPE_PL;
  154.  
  155.         /* NOTE: it could be FILE_TYPE_PL too! */
  156.         return FILE_TYPE_FILE;
  157. }
  158.  
  159. void cmus_add(add_ti_cb add, const char *name, enum file_type ft, int jt, int force,
  160.                 void *opaque)
  161. {
  162.         struct add_data *data = xnew(struct add_data, 1);
  163.  
  164.         data->add = add;
  165.         data->name = xstrdup(name);
  166.         data->type = ft;
  167.         data->force = force;
  168.         data->opaque = opaque;
  169.  
  170.         job_schedule_add(jt, data);
  171. }
  172.  
  173. static int save_ext_playlist_cb(void *data, struct track_info *ti)
  174. {
  175.         GBUF(buf);
  176.         int fd = *(int *)data;
  177.         int i, rc;
  178.  
  179.         gbuf_addf(&buf, "file %s\n", escape(ti->filename));
  180.         gbuf_addf(&buf, "duration %d\n", ti->duration);
  181.         gbuf_addf(&buf, "codec %s\n", ti->codec);
  182.         gbuf_addf(&buf, "bitrate %ld\n", ti->bitrate);
  183.         for (i = 0; ti->comments[i].key; i++)
  184.                 gbuf_addf(&buf, "tag %s %s\n",
  185.                                 ti->comments[i].key,
  186.                                 escape(ti->comments[i].val));
  187.  
  188.         rc = write_all(fd, buf.buffer, buf.len);
  189.         gbuf_free(&buf);
  190.  
  191.         if (rc == -1)
  192.                 return -1;
  193.         return 0;
  194. }
  195.  
  196. static int save_playlist_cb(void *data, struct track_info *ti)
  197. {
  198.         int fd = *(int *)data;
  199.         const char nl = '\n';
  200.         int rc;
  201.  
  202.         rc = write_all(fd, ti->filename, strlen(ti->filename));
  203.         if (rc == -1)
  204.                 return -1;
  205.         rc = write_all(fd, &nl, 1);
  206.         if (rc == -1)
  207.                 return -1;
  208.         return 0;
  209. }
  210.  
  211. static int do_cmus_save(for_each_ti_cb for_each_ti, const char *filename,
  212.                 save_tracks_cb save_tracks, void *opaque)
  213. {
  214.         int fd, rc;
  215.  
  216.         if (strcmp(filename, "-") == 0) {
  217.                 if (get_client_fd() == -1) {
  218.                         error_msg("saving to stdout works only remotely");
  219.                         return 0;
  220.                 }
  221.                 fd = dup(get_client_fd());
  222.         } else
  223.                 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
  224.         if (fd == -1)
  225.                 return -1;
  226.         rc = for_each_ti(save_tracks, &fd, opaque);
  227.         close(fd);
  228.         return rc;
  229. }
  230.  
  231. int cmus_save(for_each_ti_cb for_each_ti, const char *filename, void *opaque)
  232. {
  233.         return do_cmus_save(for_each_ti, filename, save_playlist_cb, opaque);
  234. }
  235.  
  236. int cmus_save_ext(for_each_ti_cb for_each_ti, const char *filename,
  237.                 void *opaque)
  238. {
  239.         return do_cmus_save(for_each_ti, filename, save_ext_playlist_cb,
  240.                         opaque);
  241. }
  242.  
  243. static int update_cb(void *data, struct track_info *ti)
  244. {
  245.         struct update_data *d = data;
  246.  
  247.         if (d->size == d->used) {
  248.                 if (d->size == 0)
  249.                         d->size = 16;
  250.                 d->size *= 2;
  251.                 d->ti = xrenew(struct track_info *, d->ti, d->size);
  252.         }
  253.         track_info_ref(ti);
  254.         d->ti[d->used++] = ti;
  255.         return 0;
  256. }
  257.  
  258. void cmus_update_cache(int force)
  259. {
  260.         struct update_cache_data *data;
  261.  
  262.         data = xnew(struct update_cache_data, 1);
  263.         data->force = force;
  264.  
  265.         job_schedule_update_cache(JOB_TYPE_LIB, data);
  266. }
  267.  
  268. void cmus_update_lib(void)
  269. {
  270.         struct update_data *data;
  271.  
  272.         data = xnew0(struct update_data, 1);
  273.  
  274.         lib_for_each(update_cb, data, NULL);
  275.  
  276.         job_schedule_update(data);
  277. }
  278.  
  279. void cmus_update_tis(struct track_info **tis, int nr, int force)
  280. {
  281.         struct update_data *data;
  282.  
  283.         data = xnew(struct update_data, 1);
  284.         data->size = nr;
  285.         data->used = nr;
  286.         data->ti = tis;
  287.         data->force = force;
  288.  
  289.         job_schedule_update(data);
  290. }
  291.  
  292. static const char *get_ext(const char *filename)
  293. {
  294.         const char *ext = strrchr(filename, '.');
  295.  
  296.         if (ext)
  297.                 ext++;
  298.         return ext;
  299. }
  300.  
  301. static int str_in_array(const char *str, const char * const *array)
  302. {
  303.         int i;
  304.  
  305.         for (i = 0; array[i]; i++) {
  306.                 if (strcasecmp(str, array[i]) == 0)
  307.                         return 1;
  308.         }
  309.         return 0;
  310. }
  311.  
  312. int cmus_is_playlist(const char *filename)
  313. {
  314.         const char *ext = get_ext(filename);
  315.  
  316.         return ext && str_in_array(ext, playlist_exts);
  317. }
  318.  
  319. int cmus_is_playable(const char *filename)
  320. {
  321.         const char *ext = get_ext(filename);
  322.  
  323.         return ext && str_in_array(ext, (const char * const *)playable_exts);
  324. }
  325.  
  326. int cmus_is_supported(const char *filename)
  327. {
  328.         const char *ext = get_ext(filename);
  329.  
  330.         return ext && (str_in_array(ext, (const char * const *)playable_exts) ||
  331.                         str_in_array(ext, playlist_exts));
  332. }
  333.  
  334. struct pl_data {
  335.         int (*cb)(void *data, const char *line);
  336.         void *data;
  337. };
  338.  
  339. static int pl_handle_line(void *data, const char *line)
  340. {
  341.         struct pl_data *d = data;
  342.         int i = 0;
  343.  
  344.         while (isspace((unsigned char)line[i]))
  345.                 i++;
  346.         if (line[i] == 0)
  347.                 return 0;
  348.  
  349.         if (line[i] == '#')
  350.                 return 0;
  351.  
  352.         return d->cb(d->data, line);
  353. }
  354.  
  355. static int pls_handle_line(void *data, const char *line)
  356. {
  357.         struct pl_data *d = data;
  358.  
  359.         if (strncasecmp(line, "file", 4))
  360.                 return 0;
  361.         line = strchr(line, '=');
  362.         if (line == NULL)
  363.                 return 0;
  364.         return d->cb(d->data, line + 1);
  365. }
  366.  
  367. int cmus_playlist_for_each(const char *buf, int size, int reverse,
  368.                 int (*cb)(void *data, const char *line),
  369.                 void *data)
  370. {
  371.         struct pl_data d = { cb, data };
  372.         int (*handler)(void *, const char *);
  373.  
  374.         handler = pl_handle_line;
  375.         if (size >= 10 && strncasecmp(buf, "[playlist]", 10) == 0)
  376.                 handler = pls_handle_line;
  377.  
  378.         if (reverse) {
  379.                 buffer_for_each_line_reverse(buf, size, handler, &d);
  380.         } else {
  381.                 buffer_for_each_line(buf, size, handler, &d);
  382.         }
  383.         return 0;
  384. }
  385.  
  386. /* multi-threaded next track requests */
  387.  
  388. #define cmus_next_file_lock() cmus_mutex_lock(&cmus_next_file_mutex)
  389. #define cmus_next_file_unlock() cmus_mutex_unlock(&cmus_next_file_mutex)
  390.  
  391. static struct track_info *cmus_get_next_from_main_thread(void)
  392. {
  393.         struct track_info *ti = play_queue_remove();
  394.         if (!ti)
  395.                 ti = play_library ? lib_goto_next() : pl_goto_next();
  396.         return ti;
  397. }
  398.  
  399. static struct track_info *cmus_get_next_from_other_thread(void)
  400. {
  401.         static pthread_mutex_t mutex = CMUS_MUTEX_INITIALIZER;
  402.         cmus_mutex_lock(&mutex);
  403.  
  404.         /* only one thread may request a track at a time */
  405.  
  406.         notify_via_pipe(cmus_next_track_request_fd_priv);
  407.  
  408.         cmus_next_file_lock();
  409.         while (!cmus_next_file_provided)
  410.                 pthread_cond_wait(&cmus_next_file_cond, &cmus_next_file_mutex);
  411.         struct track_info *ti = cmus_next_file;
  412.         cmus_next_file_provided = 0;
  413.         cmus_next_file_unlock();
  414.  
  415.         cmus_mutex_unlock(&mutex);
  416.  
  417.         return ti;
  418. }
  419.  
  420. struct track_info *cmus_get_next_track(void)
  421. {
  422.         pthread_t this_thread = pthread_self();
  423.         if (pthread_equal(this_thread, main_thread))
  424.                 return cmus_get_next_from_main_thread();
  425.         return cmus_get_next_from_other_thread();
  426. }
  427.  
  428. void cmus_provide_next_track(void)
  429. {
  430.         clear_pipe(cmus_next_track_request_fd, 1);
  431.  
  432.         cmus_next_file_lock();
  433.         cmus_next_file = cmus_get_next_from_main_thread();
  434.         cmus_next_file_provided = 1;
  435.         cmus_next_file_unlock();
  436.  
  437.         pthread_cond_broadcast(&cmus_next_file_cond);
  438. }
  439.  
  440. void cmus_track_request_init(void)
  441. {
  442.         init_pipes(&cmus_next_track_request_fd, &cmus_next_track_request_fd_priv);
  443. }
  444.  
downloadcmus.c Source code - Download cmus Source code
Related Source Codes/Software:
Ink - An HTML5/CSS3 framework used at SAPO for fast and ... 2017-04-22
memory-stats.js - minimal monitor for JS Heap Size via performance.m... 2017-04-22
LayoutKit - LayoutKit is a fast view layout library for iOS, m... 2017-04-22
Cpp-Primer - C++ Primer 5 answer 2017-04-22
RBBAnimation - Block-based animations made easy, comes with easin... 2017-04-22
phpDocumentor2 - Documentation Generator for PHP ... 2017-04-22
flexboxfroggy - A game for learning CSS flexbox ... 2017-04-22
wicked - Use wicked to turn your controller into a wizard ... 2017-04-22
Begin-Latex-in-minutes - Brief Intro to LaTeX for beginners that helps you ... 2017-04-22
guard-livereload - Guard::LiveReload automatically reload your browse... 2017-04-22
swifter - Tiny http server engine written in Swift programmi... 2017-04-29
Chartbuilder - A front-end charting application that facilitates ... 2017-04-29
offline-first - 2017-04-29
plotly.py - An interactive, browser-based graphing library for... 2017-04-29
statsmodels - Statsmodels: statistical modeling and econometrics... 2017-04-29
android-maps-utils - Handy extensions to the Google Maps Android API. 2017-04-29
enyo - A JavaScript application framework emphasizing mod... 2017-04-29
darkforestGo - DarkForest, the Facebook Go engine. 2017-04-29
erpnext - ERP made Simple http://erpn... 2017-04-29
SwiftInFlux - An attempt to gather all that is in flux in Swift. 2017-04-29

 Back to top