Updated year to 2010 in copyright notes.
[shell-fm.git] / source / radio.c
blob0bb9e1967b9c87342748573a7f3c3d8e1d45eecb
1 /*
2 Copyright (C) 2006-2010 by Jonas Kramer.
3 Published under the terms of the GNU General Public License (GPL).
4 */
6 #define _GNU_SOURCE
9 #include <stdio.h>
10 #include <termios.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <stdlib.h>
15 #include <fcntl.h>
16 #include <sys/time.h>
17 #include <signal.h>
18 #include <stdarg.h>
19 #include <assert.h>
21 #include "completion.h"
22 #include "http.h"
23 #include "settings.h"
24 #include "readline.h"
25 #include "history.h"
26 #include "split.h"
27 #include "feeds.h"
28 #include "strary.h"
29 #include "service.h"
30 #include "tag.h"
31 #include "globals.h"
34 extern char ** popular;
35 static int radiocomplete(char *, const unsigned, int);
37 static char ** users = NULL, ** artists = NULL, ** overall = NULL;
40 Prompt for a Last.FM radio station URI, providing kind of smart tab
41 completion and a history. No return value, playback of the URI is started
42 directly from here.
44 void radioprompt(const char * prompt) {
45 char * url, * decoded = NULL;
47 struct prompt setup = {
48 .prompt = prompt,
49 .line = NULL,
50 .history = uniq(slurp(rcpath("radio-history"))),
51 .callback = radiocomplete,
54 /* Get overall top tags. */
55 overall = overalltags();
57 /* Get user, friends and neighbors. */
58 users = neighbors(value(& rc, "username"));
59 users = merge(users, friends(value(& rc, "username")), 0);
60 users = append(users, value(& rc, "username"));
62 /* Get top artists. */
63 artists = topartists(value(& rc, "username"));
65 /* Read the line. */
66 url = readline(& setup);
68 /* Free everything. */
69 purge(users);
70 purge(artists);
71 purge(overall);
73 overall = users = artists = NULL;
75 if(setup.history)
76 purge(setup.history);
78 decode(url, & decoded);
80 station(decoded);
82 free(decoded);
86 /* Callback for the radio prompt for smart completion of radio URIs. */
87 int radiocomplete(char * line, const unsigned max, int changed) {
88 unsigned length = strlen(line), nsplt = 0, slash = 0, nres = 0;
89 const char * match;
90 char ** splt, * types [] = {
91 "user",
92 "usertags",
93 "artist",
94 "globaltags",
95 "play",
96 NULL
99 /* Remove leading "lastfm://", if found. */
100 if(!strncasecmp(line, "lastfm://", 9)) {
101 memmove(line, line + 9, 9);
102 memset(line + 9, 0, max - (length -= 9));
105 if(length > 0 && line[length - 1] == '/') {
106 slash = !0;
107 changed = !0;
110 splt = split(line, "/", & nsplt);
112 if(!nsplt) {
113 free(splt);
114 return 0;
117 switch(nsplt + (slash ? 1 : 0)) {
119 /* First level completions (user, usertags, artists, ...) */
120 case 1:
122 /* Get next match from first level chunks and fill it in. */
123 if((match = nextmatch(types, changed ? splt[0] : NULL, & nres)) != NULL) {
124 snprintf(line, max, "%s%s", match, nres == 1 ? "/" : "");
127 break;
129 /* Second level completions (user/$USER, globaltags/$TAG, ...) */
130 case 2:
131 /* For URIs like "{user,usertags}/...". */
132 if(!strcmp(splt[0], "user") || !strcmp(splt[0], "usertags")) {
134 /* Get next match for 2nd level user chunk (user) and fill it in. */
135 match = nextmatch(users, changed ? (slash ? "" : splt[1]) : NULL, & nres);
137 if(match)
138 snprintf(line, max, "%s/%s%s", splt[0], match, nres == 1 ? "/" : "");
141 /* For URIs like "artist/...". */
142 else if(!strcmp(splt[0], "artist")) {
144 /* Get next artist match for 2nd level. */
145 match = nextmatch(artists, changed ? (slash ? "" : splt[1]) : NULL, & nres);
147 if(match)
148 snprintf(line, max, "%s/%s%s", splt[0], match, nres == 1 ? "/" : "");
152 For URIs like "globaltags/...". Simply tag completion applied
153 here.
155 else if(!strcmp(splt[0], "globaltags")) {
156 char * lastchunk = strrchr(line, '/') + 1;
157 popular = overalltags();
158 tagcomplete(lastchunk, max - (lastchunk - line), changed);
159 purge(popular);
161 break;
163 /* Third level completions (artist/$ARTIST/fans, ...) */
164 case 3:
165 /* "user/$USER/{personal,neighbors,loved,recommended,playlist}" */
166 if(!strcmp(splt[0], "user")) {
167 char * radios [] = {
168 "personal",
169 "neighbours",
170 "loved",
171 "recommended",
172 "playlist",
173 NULL
176 /* Get next match for 3rd level chunk and fill it in. */
177 match = nextmatch(radios, changed ? (slash ? "" : splt[2]) : NULL, NULL);
178 snprintf(line, max, "%s/%s/%s", splt[0], splt[1], match ? match : splt[2]);
181 /* "artist/$ARTIST/{fans,similarartists}" */
182 else if(!strcmp(splt[0], "artist")) {
183 char * radios [] = {
184 "fans",
185 "similarartists",
186 NULL
189 /* Get next match for 3rd level chunk. */
190 match = nextmatch(radios, changed ? (slash ? "" : splt[2]) : NULL, NULL);
191 snprintf(line, max, "%s/%s/%s", splt[0], splt[1], match ? match : splt[2]);
194 /* Simple tag completion for "usertags" stations. */
195 else if(!strcmp(splt[0], "usertags")) {
196 char * lastchunk = strrchr(line, '/') + 1;
198 popular = overalltags();
199 tagcomplete(lastchunk, max - (lastchunk - line), changed);
200 purge(popular);
203 break;
206 while(nsplt--)
207 free(splt[nsplt]);
209 free(splt);
211 return !0;