PageRenderTime 284ms CodeModel.GetById 101ms app.highlight 63ms RepoModel.GetById 118ms app.codeStats 0ms

/lib/Teto/DAAP.pm

http://github.com/motemen/Teto
Perl | 132 lines | 94 code | 33 blank | 5 comment | 4 complexity | af1e8cf0fb1174822f523c8b987fb7eb MD5 | raw file
  1package Teto::DAAP;
  2use Mouse;
  3use Teto::Playlist;
  4use Teto::Track;
  5use AnyEvent::DAAP::Server;
  6use Sys::Hostname;
  7
  8has daap_server => (
  9    is  => 'rw',
 10    isa => 'AnyEvent::DAAP::Server',
 11    default => sub { AnyEvent::DAAP::Server->new(port => 13689, name => 'teto ' . hostname) },
 12);
 13
 14__PACKAGE__->meta->make_immutable;
 15
 16no Mouse;
 17
 18### XXX experimental. to enable this, run teto.pl with --enable_daap.
 19# depends on https://github.com/motemen/AnyEvent-DAAP-Server
 20
 21# TODO
 22# - preload tracks
 23# - update track length
 24
 25sub BUILD {
 26    my $self = shift;
 27    $self->daap_server->setup;
 28
 29    Teto::Playlist->meta->add_after_method_modifier(
 30        feed_url => sub {
 31            my $playlist = shift;
 32            $self->daap_server->add_track($_->as_daap_track) for $playlist->tracks;
 33            $self->daap_server->add_playlist($playlist->as_daap_playlist);
 34            $self->daap_server->database_updated;
 35        }
 36    );
 37}
 38
 39package AnyEvent::DAAP::Server::Track::Teto;
 40use Mouse;
 41use Coro;
 42
 43extends 'AnyEvent::DAAP::Server::Track';
 44
 45has track => (
 46    is  => 'rw',
 47    isa => 'Teto::Track',
 48    required => 1,
 49);
 50
 51__PACKAGE__->meta->make_immutable;
 52
 53no Mouse;
 54
 55sub allow_range { 1 }
 56
 57sub write_data {
 58    my ($self, $connection, $res, $pos) = @_;
 59
 60    my $track = $self->track;
 61    $track->log(debug => 'daap: start streaming');
 62
 63    async {
 64        $Coro::current->{desc} = 'daap track play';
 65        $track->prepare;
 66        $track->play;
 67    };
 68
 69    async {
 70        $Coro::current->{desc} = 'daap track write';
 71
 72        until ($track->is_done) {
 73            $track->buffer_signal->wait;
 74        }
 75
 76        my $buf = substr $track->buffer, $pos || 0;
 77        $res->content_length(length $buf);
 78        $res->content($buf);
 79        $self->push_response($connection, $res);
 80    };
 81}
 82
 83package Teto::Track;
 84
 85sub dmap_track_id {
 86    my $self = shift;
 87    return $self->track_id & 0xFFFFFF; # XXX this field is 3 byte long
 88}
 89
 90sub as_daap_track {
 91    my $self = shift;
 92    my $track = AnyEvent::DAAP::Server::Track::Teto->new(track => $self);
 93
 94    $track->dmap_itemid($self->dmap_track_id);
 95    $track->dmap_containeritemid($self->track_id);
 96
 97    $track->dmap_itemkind(2);
 98    $track->dmap_persistentid($self->track_id);
 99
100    $track->daap_songbitrate(192 * 1000);
101    $track->daap_songsamplerate(44100 * 1000);
102
103    $track->dmap_itemname($self->title || $self->track_id);
104
105    $track->daap_songdateadded(time());
106    $track->daap_songdatemodified(time());
107    $track->daap_songformat('mp3');
108    $track->daap_songsize($self->peek_buffer_length || (59 * 60 + 59) * 192 / 8); 
109
110    $track->daap_songtime((59 * 60 + 59) * 1000); # 59:59 dummy, to enable seek
111
112    return $track;
113}
114
115package Teto::Playlist;
116use AnyEvent::DAAP::Server::Playlist;
117
118sub dmap_playlist_id {
119    my $self = shift;
120    return 0+$self & 0xFFFFFF;
121}
122
123sub as_daap_playlist {
124    my $self = shift;
125    my $playlist = AnyEvent::DAAP::Server::Playlist->new;
126    $playlist->dmap_itemname($self->title);
127    $playlist->dmap_itemid($self->dmap_playlist_id);
128    $playlist->tracks([ map { $_->as_daap_track } $self->tracks ]);
129    return $playlist;
130}
131
1321;