1/*
2 * Copyright (C) 2010-2011 Julien BLACHE <jb@jblache.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <string.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <limits.h>
31
32#include <libavcodec/avcodec.h>
33#include <libavformat/avformat.h>
34#include <libswscale/swscale.h>
35
36#include "event.h"
37#include "db.h"
38#include "logger.h"
39#if LIBAVFORMAT_VERSION_MAJOR >= 53
40# include "avio_evbuffer.h"
41#endif
42#include "artwork.h"
43
44
45static const char *cover_basename[] =
46  {
47    "artwork", "cover",
48  };
49
50static const char *cover_extension[] =
51  {
52    "png", "jpg",
53  };
54
55
56static int
57artwork_read(char *filename, struct evbuffer *evbuf)
58{
59  uint8_t buf[4096];
60  struct stat sb;
61  int fd;
62  int ret;
63
64  fd = open(filename, O_RDONLY);
65  if (fd < 0)
66    {
67      DPRINTF(E_WARN, L_ART, "Could not open artwork file '%s': %s\n", filename, strerror(errno));
68
69      return -1;
70    }
71
72  ret = fstat(fd, &sb);
73  if (ret < 0)
74    {
75      DPRINTF(E_WARN, L_ART, "Could not stat() artwork file '%s': %s\n", filename, strerror(errno));
76
77      goto out_fail;
78    }
79
80  ret = evbuffer_expand(evbuf, sb.st_size);
81  if (ret < 0)
82    {
83      DPRINTF(E_LOG, L_ART, "Out of memory for artwork\n");
84
85      goto out_fail;
86    }
87
88  while ((ret = read(fd, buf, sizeof(buf))) > 0)
89    evbuffer_add(evbuf, buf, ret);
90
91  close(fd);
92
93  return 0;
94
95 out_fail:
96  close(fd);
97  return -1;
98}
99
100static int
101artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int format, struct evbuffer *evbuf)
102{
103  uint8_t *buf;
104  uint8_t *outbuf;
105
106  AVCodecContext *src;
107
108  AVFormatContext *dst_ctx;
109  AVCodecContext *dst;
110  AVOutputFormat *dst_fmt;
111  AVStream *dst_st;
112
113  AVCodec *img_decoder;
114  AVCodec *img_encoder;
115
116  int64_t pix_fmt_mask;
117  const enum PixelFormat *pix_fmts;
118
119  AVFrame *i_frame;
120  AVFrame *o_frame;
121
122  struct SwsContext *swsctx;
123
124  AVPacket pkt;
125  int have_frame;
126
127  int outbuf_len;
128
129  int ret;
130
131  src = src_ctx->streams[s]->codec;
132
133  img_decoder = avcodec_find_decoder(src->codec_id);
134  if (!img_decoder)
135    {
136      DPRINTF(E_LOG, L_ART, "No suitable decoder found for artwork %s\n", src_ctx->filename);
137
138      return -1;
139    }
140
141  ret = avcodec_open2(src, img_decoder,NULL);
142  if (ret < 0)
143    {
144      DPRINTF(E_LOG, L_ART, "Could not open codec for decoding: %s\n", strerror(AVUNERROR(ret)));
145
146      return -1;
147    }
148
149  /* Set up output */
150#if LIBAVFORMAT_VERSION_MAJOR >= 53 || (LIBAVFORMAT_VERSION_MAJOR == 52 && LIBAVFORMAT_VERSION_MINOR >= 45)
151  /* FFmpeg 0.6 */
152  dst_fmt = av_guess_format("image2", NULL, NULL);
153#else
154  dst_fmt = guess_format("image2", NULL, NULL);
155#endif
156  if (!dst_fmt)
157    {
158      DPRINTF(E_LOG, L_ART, "ffmpeg image2 muxer not available\n");
159
160      ret = -1;
161      goto out_close_src;
162    }
163
164  dst_fmt->video_codec = CODEC_ID_NONE;
165
166  /* Try to keep same codec if possible */
167  if ((src->codec_id == CODEC_ID_PNG) && (format & ART_CAN_PNG))
168    dst_fmt->video_codec = CODEC_ID_PNG;
169  else if ((src->codec_id == CODEC_ID_MJPEG) && (format & ART_CAN_JPEG))
170    dst_fmt->video_codec = CODEC_ID_MJPEG;
171
172  /* If not possible, select new codec */
173  if (dst_fmt->video_codec == CODEC_ID_NONE)
174    {
175      if (format & ART_CAN_PNG)
176	dst_fmt->video_codec = CODEC_ID_PNG;
177      else if (format & ART_CAN_JPEG)
178	dst_fmt->video_codec = CODEC_ID_MJPEG;
179    }
180
181  img_encoder = avcodec_find_encoder(dst_fmt->video_codec);
182  if (!img_encoder)
183    {
184      DPRINTF(E_LOG, L_ART, "No suitable encoder found for codec ID %d\n", dst_fmt->video_codec);
185
186      ret = -1;
187      goto out_close_src;
188    }
189
190  dst_ctx = avformat_alloc_context();
191  if (!dst_ctx)
192    {
193      DPRINTF(E_LOG, L_ART, "Out of memory for format context\n");
194
195      ret = -1;
196      goto out_close_src;
197    }
198
199  dst_ctx->oformat = dst_fmt;
200
201#if LIBAVFORMAT_VERSION_MAJOR >= 53
202  dst_fmt->flags &= ~AVFMT_NOFILE;
203#else
204  ret = snprintf(dst_ctx->filename, sizeof(dst_ctx->filename), "evbuffer:%p", evbuf);
205  if ((ret < 0) || (ret >= sizeof(dst_ctx->filename)))
206    {
207      DPRINTF(E_LOG, L_ART, "Output artwork URL too long\n");
208
209      ret = -1;
210      goto out_free_dst_ctx;
211    }
212#endif
213
214  dst_st = avformat_new_stream(dst_ctx,NULL);
215  if (!dst_st)
216    {
217      DPRINTF(E_LOG, L_ART, "Out of memory for new output stream\n");
218
219      ret = -1;
220      goto out_free_dst_ctx;
221    }
222
223  dst = dst_st->codec;
224
225#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64)
226  avcodec_get_context_defaults3(dst, NULL);
227#else
228  avcodec_get_context_defaults3(dst, NULL);
229#endif
230
231  if (dst_fmt->flags & AVFMT_GLOBALHEADER)
232    dst->flags |= CODEC_FLAG_GLOBAL_HEADER;
233
234  dst->codec_id = dst_fmt->video_codec;
235#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64)
236  dst->codec_type = AVMEDIA_TYPE_VIDEO;
237#else
238  dst->codec_type = CODEC_TYPE_VIDEO;
239#endif
240
241  pix_fmt_mask = 0;
242  pix_fmts = img_encoder->pix_fmts;
243  while (pix_fmts && (*pix_fmts != -1))
244    {
245      pix_fmt_mask |= (1 << *pix_fmts);
246      pix_fmts++;
247    }
248
249  dst->pix_fmt = avcodec_find_best_pix_fmt(pix_fmt_mask, src->pix_fmt, 1, NULL);
250
251  if (dst->pix_fmt < 0)
252    {
253      DPRINTF(E_LOG, L_ART, "Could not determine best pixel format\n");
254
255      ret = -1;
256      goto out_free_dst;
257    }
258
259  DPRINTF(E_DBG, L_ART, "Selected pixel format: %d\n", dst->pix_fmt);
260
261  dst->time_base.num = 1;
262  dst->time_base.den = 25;
263
264  dst->width = out_w;
265  dst->height = out_h;
266
267#if LIBAVFORMAT_VERSION_MAJOR <= 52 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR <= 1)
268  ret = av_set_parameters(dst_ctx, NULL);
269  if (ret < 0)
270    {
271      DPRINTF(E_LOG, L_ART, "Invalid parameters for artwork output: %s\n", strerror(AVUNERROR(ret)));
272
273      ret = -1;
274      goto out_free_dst;
275    }
276#endif
277
278  /* Open encoder */
279  ret = avcodec_open2(dst, img_encoder,NULL);
280  if (ret < 0)
281    {
282      DPRINTF(E_LOG, L_ART, "Could not open codec for encoding: %s\n", strerror(AVUNERROR(ret)));
283
284      ret = -1;
285      goto out_free_dst;
286    }
287
288  i_frame = avcodec_alloc_frame();
289  o_frame = avcodec_alloc_frame();
290
291  if (!i_frame || !o_frame)
292    {
293      DPRINTF(E_LOG, L_ART, "Could not allocate input/output frame\n");
294
295      ret = -1;
296      goto out_free_frames;
297    }
298
299  ret = avpicture_get_size(dst->pix_fmt, src->width, src->height);
300
301  DPRINTF(E_DBG, L_ART, "Artwork buffer size: %d\n", ret);
302
303  buf = (uint8_t *)av_malloc(ret);
304  if (!buf)
305    {
306      DPRINTF(E_LOG, L_ART, "Out of memory for artwork buffer\n");
307
308      ret = -1;
309      goto out_free_frames;
310    }
311
312  avpicture_fill((AVPicture *)o_frame, buf, dst->pix_fmt, src->width, src->height);
313
314  swsctx = sws_getContext(src->width, src->height, src->pix_fmt,
315			  dst->width, dst->height, dst->pix_fmt,
316			  SWS_BICUBIC, NULL, NULL, NULL);
317  if (!swsctx)
318    {
319      DPRINTF(E_LOG, L_ART, "Could not get SWS context\n");
320
321      ret = -1;
322      goto out_free_buf;
323    }
324
325  /* Get frame */
326  have_frame = 0;
327  while (av_read_frame(src_ctx, &pkt) == 0)
328    {
329      if (pkt.stream_index != s)
330	{
331	  av_free_packet(&pkt);
332	  continue;
333	}
334
335#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 32)
336      /* FFmpeg 0.6 */
337      avcodec_decode_video2(src, i_frame, &have_frame, &pkt);
338#else
339      avcodec_decode_video(src, i_frame, &have_frame, pkt.data, pkt.size);
340#endif
341
342      break;
343    }
344
345  if (!have_frame)
346    {
347      DPRINTF(E_LOG, L_ART, "Could not decode artwork\n");
348
349      av_free_packet(&pkt);
350      sws_freeContext(swsctx);
351
352      ret = -1;
353      goto out_free_buf;
354    }
355
356  /* Scale */
357#if LIBSWSCALE_VERSION_MAJOR >= 1 || (LIBSWSCALE_VERSION_MAJOR == 0 && LIBSWSCALE_VERSION_MINOR >= 9)
358  /* FFmpeg 0.6, libav 0.6+ */
359  sws_scale(swsctx, (const uint8_t * const *)i_frame->data, i_frame->linesize, 0, src->height, o_frame->data, o_frame->linesize);
360#else
361  sws_scale(swsctx, i_frame->data, i_frame->linesize, 0, src->height, o_frame->data, o_frame->linesize);
362#endif
363
364  sws_freeContext(swsctx);
365  av_free_packet(&pkt);
366
367  /* Open output file */
368#if LIBAVFORMAT_VERSION_MAJOR >= 53
369  dst_ctx->pb = avio_evbuffer_open(evbuf);
370#else
371  ret = url_fopen(&dst_ctx->pb, dst_ctx->filename, URL_WRONLY);
372#endif
373  if (ret < 0)
374    {
375      DPRINTF(E_LOG, L_ART, "Could not open artwork destination buffer\n");
376
377      ret = -1;
378      goto out_free_buf;
379    }
380
381  /* Encode frame */
382  outbuf_len = dst->width * dst->height * 3;
383  if (outbuf_len < FF_MIN_BUFFER_SIZE)
384    outbuf_len = FF_MIN_BUFFER_SIZE;
385
386  outbuf = (uint8_t *)av_malloc(outbuf_len);
387  if (!outbuf)
388    {
389      DPRINTF(E_LOG, L_ART, "Out of memory for encoded artwork buffer\n");
390
391#if LIBAVFORMAT_VERSION_MAJOR >= 53
392      avio_evbuffer_close(dst_ctx->pb);
393#else
394      url_fclose(dst_ctx->pb);
395#endif
396
397      ret = -1;
398      goto out_free_buf;
399    }
400
401  ret = avcodec_encode_video(dst, outbuf, outbuf_len, o_frame);
402  if (ret <= 0)
403    {
404      DPRINTF(E_LOG, L_ART, "Could not encode artwork\n");
405
406      ret = -1;
407      goto out_fclose_dst;
408    }
409
410  av_init_packet(&pkt);
411  pkt.stream_index = 0;
412  pkt.data = outbuf;
413  pkt.size = ret;
414
415#if LIBAVFORMAT_VERSION_MAJOR >= 53 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 3)
416  ret = avformat_write_header(dst_ctx, NULL);
417#else
418  ret = av_write_header(dst_ctx);
419#endif
420  if (ret != 0)
421    {
422      DPRINTF(E_LOG, L_ART, "Could not write artwork header: %s\n", strerror(AVUNERROR(ret)));
423
424      ret = -1;
425      goto out_fclose_dst;
426    }
427
428  ret = av_interleaved_write_frame(dst_ctx, &pkt);
429
430  if (ret != 0)
431    {
432      DPRINTF(E_LOG, L_ART, "Error writing artwork\n");
433
434      ret = -1;
435      goto out_fclose_dst;
436    }
437
438  ret = av_write_trailer(dst_ctx);
439  if (ret != 0)
440    {
441      DPRINTF(E_LOG, L_ART, "Could not write artwork trailer: %s\n", strerror(AVUNERROR(ret)));
442
443      ret = -1;
444      goto out_fclose_dst;
445    }
446
447  switch (dst_fmt->video_codec)
448    {
449      case CODEC_ID_PNG:
450	ret = ART_FMT_PNG;
451	break;
452
453      case CODEC_ID_MJPEG:
454	ret = ART_FMT_JPEG;
455	break;
456
457      default:
458	DPRINTF(E_LOG, L_ART, "Unhandled rescale output format\n");
459	ret = -1;
460	break;
461    }
462
463 out_fclose_dst:
464#if LIBAVFORMAT_VERSION_MAJOR >= 53
465  avio_evbuffer_close(dst_ctx->pb);
466#else
467  url_fclose(dst_ctx->pb);
468#endif
469  av_free(outbuf);
470
471 out_free_buf:
472  av_free(buf);
473
474 out_free_frames:
475  if (i_frame)
476    av_free(i_frame);
477  if (o_frame)
478    av_free(o_frame);
479  avcodec_close(dst);
480
481 out_free_dst:
482  av_free(dst_st);
483  av_free(dst);
484
485 out_free_dst_ctx:
486  av_free(dst_ctx);
487
488 out_close_src:
489  avcodec_close(src);
490
491  return ret;
492}
493
494static int
495artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *evbuf)
496{
497  AVFormatContext *src_ctx;
498  AVCodecContext *src;
499  int s;
500  int target_w;
501  int target_h;
502  int need_rescale;
503  int format_ok;
504  int ret;
505
506  DPRINTF(E_DBG, L_ART, "Artwork request parameters: max w = %d, max h = %d\n", max_w, max_h);
507
508  src_ctx = NULL;
509
510#if LIBAVFORMAT_VERSION_MAJOR >= 53 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 3)
511  ret = avformat_open_input(&src_ctx, filename, NULL, NULL);
512#else
513  ret = av_open_input_file(&src_ctx, filename, NULL, 0, NULL);
514#endif
515  if (ret < 0)
516    {
517      DPRINTF(E_WARN, L_ART, "Cannot open artwork file '%s': %s\n", filename, strerror(AVUNERROR(ret)));
518
519      return -1;
520    }
521
522  ret = avformat_find_stream_info(src_ctx,NULL);
523  if (ret < 0)
524    {
525      DPRINTF(E_WARN, L_ART, "Cannot get stream info: %s\n", strerror(AVUNERROR(ret)));
526
527      av_close_input_file(src_ctx);
528      return -1;
529    }
530
531  format_ok = 0;
532  for (s = 0; s < src_ctx->nb_streams; s++)
533    {
534      if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_PNG)
535	{
536	  format_ok = (format & ART_CAN_PNG) ? ART_FMT_PNG : 0;
537	  break;
538	}
539      else if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_MJPEG)
540	{
541	  format_ok = (format & ART_CAN_JPEG) ? ART_FMT_JPEG : 0;
542	  break;
543	}
544    }
545
546  if (s == src_ctx->nb_streams)
547    {
548      DPRINTF(E_LOG, L_ART, "Artwork file '%s' not a PNG or JPEG file\n", filename);
549
550      av_close_input_file(src_ctx);
551      return -1;
552    }
553
554  src = src_ctx->streams[s]->codec;
555
556  DPRINTF(E_DBG, L_ART, "Original image '%s': w %d h %d\n", filename, src->width, src->height);
557
558  need_rescale = 1;
559
560  /* Determine width/height -- assuming max_w == max_h */
561  if ((src->width <= max_w) && (src->height <= max_h))
562    {
563      need_rescale = 0;
564
565      target_w = src->width;
566      target_h = src->height;
567    }
568  else if (src->width > src->height)
569    {
570      target_w = max_w;
571      target_h = (double)max_h * ((double)src->height / (double)src->width);
572    }
573  else if (src->height > src->width)
574    {
575      target_h = max_h;
576      target_w = (double)max_w * ((double)src->width / (double)src->height);
577    }
578  else
579    {
580      target_w = max_w;
581      target_h = max_h;
582    }
583
584  DPRINTF(E_DBG, L_ART, "Raw destination width %d height %d\n", target_w, target_h);
585
586  if (target_h > max_h)
587    target_h = max_h;
588
589  /* PNG prefers even row count */
590  target_w += target_w % 2;
591
592  if (target_w > max_w)
593    target_w = max_w - (max_w % 2);
594
595  DPRINTF(E_DBG, L_ART, "Destination width %d height %d\n", target_w, target_h);
596
597  /* Fastpath */
598  if (!need_rescale && format_ok)
599    {
600      ret = artwork_read(filename, evbuf);
601      if (ret == 0)
602	ret = format_ok;
603    }
604  else
605    ret = artwork_rescale(src_ctx, s, target_w, target_h, format, evbuf);
606
607  av_close_input_file(src_ctx);
608
609  if (ret < 0)
610    {
611      if (EVBUFFER_LENGTH(evbuf) > 0)
612	evbuffer_drain(evbuf, EVBUFFER_LENGTH(evbuf));
613    }
614
615  return ret;
616}
617
618
619static int
620artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuffer *evbuf)
621{
622  char artwork[PATH_MAX];
623  char *ptr;
624  int len;
625  int i;
626  int ret;
627
628  ret = snprintf(artwork, sizeof(artwork), "%s", path);
629  if ((ret < 0) || (ret >= sizeof(artwork)))
630    {
631      DPRINTF(E_INFO, L_ART, "Artwork path exceeds PATH_MAX\n");
632
633      return -1;
634    }
635
636  ptr = strrchr(artwork, '.');
637  if (ptr)
638    *ptr = '\0';
639
640  len = strlen(artwork);
641
642  for (i = 0; i < (sizeof(cover_extension) / sizeof(cover_extension[0])); i++)
643    {
644      ret = snprintf(artwork + len, sizeof(artwork) - len, ".%s", cover_extension[i]);
645      if ((ret < 0) || (ret >= sizeof(artwork) - len))
646	{
647	  DPRINTF(E_INFO, L_ART, "Artwork path exceeds PATH_MAX (ext %s)\n", cover_extension[i]);
648
649	  continue;
650	}
651
652      DPRINTF(E_DBG, L_ART, "Trying own artwork file %s\n", artwork);
653
654      ret = access(artwork, F_OK);
655      if (ret < 0)
656	continue;
657
658      break;
659    }
660
661  if (i == (sizeof(cover_extension) / sizeof(cover_extension[0])))
662    return -1;
663
664  return artwork_get(artwork, max_w, max_h, format, evbuf);
665}
666
667static int
668artwork_get_dir_image(char *path, int isdir, int max_w, int max_h, int format, struct evbuffer *evbuf)
669{
670  char artwork[PATH_MAX];
671  char *ptr;
672  int i;
673  int j;
674  int len;
675  int ret;
676
677  ret = snprintf(artwork, sizeof(artwork), "%s", path);
678  if ((ret < 0) || (ret >= sizeof(artwork)))
679    {
680      DPRINTF(E_INFO, L_ART, "Artwork path exceeds PATH_MAX\n");
681
682      return -1;
683    }
684
685  if (!isdir)
686    {
687      ptr = strrchr(artwork, '/');
688      if (ptr)
689	*ptr = '\0';
690    }
691
692  len = strlen(artwork);
693
694  for (i = 0; i < (sizeof(cover_basename) / sizeof(cover_basename[0])); i++)
695    {
696      for (j = 0; j < (sizeof(cover_extension) / sizeof(cover_extension[0])); j++)
697	{
698	  ret = snprintf(artwork + len, sizeof(artwork) - len, "/%s.%s", cover_basename[i], cover_extension[j]);
699	  if ((ret < 0) || (ret >= sizeof(artwork) - len))
700	    {
701	      DPRINTF(E_INFO, L_ART, "Artwork path exceeds PATH_MAX (%s.%s)\n", cover_basename[i], cover_extension[j]);
702
703	      continue;
704	    }
705
706	  DPRINTF(E_DBG, L_ART, "Trying directory artwork file %s\n", artwork);
707
708	  ret = access(artwork, F_OK);
709	  if (ret < 0)
710	    continue;
711
712	  break;
713	}
714
715      if (j < (sizeof(cover_extension) / sizeof(cover_extension[0])))
716	break;
717    }
718
719  if (i == (sizeof(cover_basename) / sizeof(cover_basename[0])))
720    return -1;
721
722  return artwork_get(artwork, max_w, max_h, format, evbuf);
723}
724
725
726int
727artwork_get_item_filename(char *filename, int max_w, int max_h, int format, struct evbuffer *evbuf)
728{
729  int ret;
730
731  /* FUTURE: look at embedded artwork */
732
733  /* Look for basename(filename).{png,jpg} */
734  ret = artwork_get_own_image(filename, max_w, max_h, format, evbuf);
735  if (ret > 0)
736    return ret;
737
738  /* Look for basedir(filename)/{artwork,cover}.{png,jpg} */
739  ret = artwork_get_dir_image(filename, 0, max_w, max_h, format, evbuf);
740  if (ret > 0)
741    return ret;
742
743  return -1;
744}
745
746int
747artwork_get_item(int id, int max_w, int max_h, int format, struct evbuffer *evbuf)
748{
749  char *filename;
750  int ret;
751
752  DPRINTF(E_DBG, L_ART, "Artwork request for item %d\n", id);
753
754  filename = db_file_path_byid(id);
755  if (!filename)
756    return -1;
757
758  ret = artwork_get_item_filename(filename, max_w, max_h, format, evbuf);
759  if (ret < 0)
760    DPRINTF(E_DBG, L_ART, "No artwork found for item id %d\n", id);
761
762  free(filename);
763
764  return ret;
765}
766
767int
768artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evbuf)
769{
770  struct query_params qp;
771  struct db_media_file_info dbmfi;
772  char *dir;
773  int got_art;
774  int ret;
775
776  DPRINTF(E_DBG, L_ART, "Artwork request for group %d\n", id);
777
778  /* Try directory artwork first */
779  memset(&qp, 0, sizeof(struct query_params));
780
781  qp.type = Q_GROUP_DIRS;
782  qp.id = id;
783
784  ret = db_query_start(&qp);
785  if (ret < 0)
786    {
787      DPRINTF(E_LOG, L_ART, "Could not start Q_GROUP_DIRS query\n");
788
789      /* Skip to invidual files artwork */
790      goto files_art;
791    }
792
793  got_art = -1;
794  while ((got_art < 0) && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir))
795    {
796      got_art = artwork_get_dir_image(dir, 1, max_w, max_h, format, evbuf);
797    }
798
799  db_query_end(&qp);
800
801  if (ret < 0)
802    DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_DIRS results\n");
803  else if (got_art > 0)
804    return got_art;
805
806
807  /* Then try individual files */
808 files_art:
809  memset(&qp, 0, sizeof(struct query_params));
810
811  qp.type = Q_GROUP_ITEMS;
812  qp.id = id;
813
814  ret = db_query_start(&qp);
815  if (ret < 0)
816    {
817      DPRINTF(E_LOG, L_ART, "Could not start Q_GROUPITEMS query\n");
818
819      return -1;
820    }
821
822  got_art = -1;
823  while ((got_art < 0) && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
824    {
825      got_art = artwork_get_own_image(dbmfi.path, max_w, max_h, format, evbuf);
826    }
827
828  db_query_end(&qp);
829
830  if (ret < 0)
831    DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUPITEMS results\n");
832  else if (got_art > 0)
833    return got_art;
834
835  return -1;
836}
837