VLCMetadataService.cpp 9.49 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

23
#include "VLCMetadataService.h"
24
#include "Media.h"
25
26
27
28
#include "Album.h"
#include "AlbumTrack.h"
#include "Artist.h"
#include "Show.h"
29

30
#include "Media.h"
31

32
VLCMetadataService::VLCMetadataService( const VLC::Instance& vlc )
33
34
35
36
37
38
    : m_instance( vlc )
    , m_cb( nullptr )
    , m_ml( nullptr )
{
}

39
bool VLCMetadataService::initialize(IMetadataServiceCb* callback, MediaLibrary* ml )
40
41
42
43
44
45
46
47
48
49
50
{
    m_cb = callback;
    m_ml = ml;
    return true;
}

unsigned int VLCMetadataService::priority() const
{
    return 100;
}

51
void VLCMetadataService::run( std::shared_ptr<Media> file, void* data )
52
{
53
54
55
56
57
58
    if ( file->duration() != -1 )
    {
        LOG_INFO( file->mrl(), " was already parsed" );
        m_cb->done( file, Status::Success, data );
        return;
    }
59
60
    LOG_INFO( "Parsing ", file->mrl() );

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
61
    auto ctx = new Context( file );
62
63
    std::unique_ptr<Context> ctxPtr( ctx );

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
64
65
    ctx->media = VLC::Media( m_instance, file->mrl(), VLC::Media::FromPath );

66
    std::unique_lock<std::mutex> lock( m_mutex );
67
    std::atomic<Status> status( Status::Unknown );
68

69
    ctx->media.eventManager().onParsedChanged([this, ctx, &status](bool parsed) {
70
        if ( parsed == false )
71
            return;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
72
        auto s = handleMediaMeta( ctx->file, ctx->media );
73
74
75
        auto expected = Status::Unknown;
        while ( status.compare_exchange_weak( expected, s ) == false )
            ;
76
        m_cond.notify_all();
77
    });
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
78
    ctx->media.parseAsync();
79
    auto success = m_cond.wait_for( lock, std::chrono::seconds( 5 ), [&status]() { return status != Status::Unknown; } );
80
    if ( success == false )
81
        m_cb->done( ctx->file, Status::Fatal, data );
82
83
    else
        m_cb->done( ctx->file, status, data );
84
85
}

86
IMetadataService::Status VLCMetadataService::handleMediaMeta( std::shared_ptr<Media> media, VLC::Media& vlcMedia ) const
87
{
88
    auto tracks = vlcMedia.tracks();
89
    if ( tracks.size() == 0 )
90
    {
91
        LOG_ERROR( "Failed to fetch tracks" );
92
        return Status::Fatal;
93
    }
94
    bool isAudio = true;
95
    for ( auto& track : tracks )
96
    {
97
98
99
        auto codec = track.codec();
        std::string fcc( (const char*)&codec, 4 );
        if ( track.type() == VLC::MediaTrack::Video )
100
101
        {
            isAudio = false;
102
            auto fps = (float)track.fpsNum() / (float)track.fpsDen();
103
            media->addVideoTrack( fcc, track.width(), track.height(), fps );
104
        }
105
        else if ( track.type() == VLC::MediaTrack::Audio )
106
        {
107
108
            media->addAudioTrack( fcc, track.bitrate(), track.rate(), track.channels(),
                                  track.language(), track.description() );
109
110
111
112
        }
    }
    if ( isAudio == true )
    {
113
        if ( parseAudioFile( media, vlcMedia ) == false )
114
            return Status::Fatal;
115
116
117
    }
    else
    {
118
        if (parseVideoFile( media, vlcMedia ) == false )
119
            return Status::Fatal;
120
    }
121
122
123
    auto duration = vlcMedia.duration();
    if ( media->setDuration( duration ) == false )
        return Status::Error;
124
    return Status::Success;
125
126
}

127
bool VLCMetadataService::parseAudioFile( std::shared_ptr<Media> media, VLC::Media& vlcMedia ) const
128
{
129
130
    media->setType( IMedia::Type::AudioType );
    auto albumTitle = vlcMedia.meta( libvlc_meta_Album );
131
    auto newAlbum = false;
132
    std::shared_ptr<Album> album;
133
    std::shared_ptr<AlbumTrack> track;
134
    if ( albumTitle.length() > 0 )
135
    {
136
        album = std::static_pointer_cast<Album>( m_ml->album( albumTitle ) );
137
        if ( album == nullptr )
138
        {
139
140
141
142
            album = m_ml->createAlbum( albumTitle );
            if ( album != nullptr )
            {
                newAlbum = true;
143
                auto date = vlcMedia.meta( libvlc_meta_Date );
144
                if ( date.length() > 0 )
145
                    album->setReleaseYear( std::stoul( date ) );
146
                auto artwork = vlcMedia.meta( libvlc_meta_ArtworkURL );
147
148
                if ( artwork.length() != 0 )
                    album->setArtworkUrl( artwork );
149
            }
150
        }
151
152
        if ( album != nullptr )
        {
153
            track = handleTrack( album, media, vlcMedia );
154
155
156
            if ( track != nullptr )
                media->setAlbumTrack( track );
        }
157
    }
158

159
    return handleArtist( album, media, vlcMedia, newAlbum );
160
161
}

162
bool VLCMetadataService::parseVideoFile( std::shared_ptr<Media> file, VLC::Media& media ) const
163
{
164
    file->setType( IMedia::Type::VideoType );
165
    auto title = media.meta( libvlc_meta_Title );
166
167
    if ( title.length() == 0 )
        return true;
168
    auto showName = media.meta( libvlc_meta_ShowName );
169
170
    if ( showName.length() == 0 )
    {
171
        auto show = m_ml->show( showName );
172
173
        if ( show == nullptr )
        {
174
            show = m_ml->createShow( showName );
175
176
177
            if ( show == nullptr )
                return false;
        }
178

179
        auto episodeIdStr = media.meta( libvlc_meta_Episode );
180
181
182
183
184
185
        if ( episodeIdStr.length() > 0 )
        {
            size_t endpos;
            int episodeId = std::stoi( episodeIdStr, &endpos );
            if ( endpos != episodeIdStr.length() )
            {
186
                LOG_ERROR( "Invalid episode id provided" );
187
188
                return true;
            }
189
190
            std::shared_ptr<Show> s = std::static_pointer_cast<Show>( show );
            s->addEpisode( title, episodeId );
191
192
193
194
195
196
197
        }
    }
    else
    {
        // How do we know if it's a movie or a random video?
    }
    return true;
198
}
199

200
bool VLCMetadataService::handleArtist( std::shared_ptr<Album> album, std::shared_ptr<Media> media, VLC::Media& vlcMedia, bool newAlbum ) const
201
{
202
203
    assert(media != nullptr);

204
    auto newArtist = false;
205
    auto albumArtistName = vlcMedia.meta( libvlc_meta_AlbumArtist );
206
207
208
209
    auto artistName = vlcMedia.meta( libvlc_meta_Artist );
    if ( albumArtistName.empty() == true )
        albumArtistName = artistName;

210
    if ( albumArtistName.length() > 0 )
211
    {
212
        auto artist = std::static_pointer_cast<Artist>( m_ml->artist( albumArtistName ) );
213
214
        if ( artist == nullptr )
        {
215
            artist = m_ml->createArtist( albumArtistName );
216
217
            if ( artist == nullptr )
            {
218
                LOG_ERROR( "Failed to create new artist ", albumArtistName );
219
220
221
222
223
                // Consider this a minor failure and go on nevertheless
                return true;
            }
            newArtist = true;
        }
224
        artist->addMedia( media.get() );
225
        // If this is either a new album or a new artist, we need to add the relationship between the two.
226
        if ( album != nullptr && ( newAlbum == true || newArtist == true ) )
227
228
229
        {
            if ( album->addArtist( artist ) == false )
                LOG_WARN( "Failed to add artist ", artist->name(), " to album ", album->title() );
230
231
232
            // This is the first time we have both artist & album, use this opportunity to set an artist artwork.
            if ( artist->artworkUrl().empty() == true )
                artist->setArtworkUrl( album->artworkUrl() );
233
234
        }
    }
235
    else
236
    {
237
        std::static_pointer_cast<Artist>( m_ml->unknownArtist() )->addMedia( media.get() );
238
239
240
        // If we get here, it means we have neither artist nor albumartist tags. We can't do much more.
        return true;
    }
241
242
243
244

    if ( artistName.length() > 0 )
        media->setArtist( artistName );
    else if ( albumArtistName.length() > 0 )
245
    {
246
247
248
        // Always provide an artist, to avoid the user from having to fallback
        // to the album artist by himself
        media->setArtist( albumArtistName );
249
    }
250
251
    return true;
}
252

253
std::shared_ptr<AlbumTrack> VLCMetadataService::handleTrack(std::shared_ptr<Album> album, std::shared_ptr<Media> media, VLC::Media& vlcMedia) const
254
{
255
    auto trackNbStr = vlcMedia.meta( libvlc_meta_TrackNumber );
256

257
    auto title = vlcMedia.meta( libvlc_meta_Title );
258
    if ( title.empty() == true )
259
260
    {
        LOG_WARN( "Failed to get track title" );
261
262
263
264
265
        if ( trackNbStr.empty() == false )
        {
            title = "Track #";
            title += trackNbStr;
        }
266
    }
267
268
269
270
271
272
273
    if ( title.empty() == false )
        media->setTitle( title );
    unsigned int trackNb;
    if ( trackNbStr.empty() == false )
        trackNb = std::stoi( trackNbStr );
    else
        trackNb = 0;
274
    auto track = std::static_pointer_cast<AlbumTrack>( album->addTrack( media, trackNb ) );
275
276
277
278
279
    if ( track == nullptr )
    {
        LOG_ERROR( "Failed to create album track" );
        return nullptr;
    }
280
    auto genre = vlcMedia.meta( libvlc_meta_Genre );
281
    if ( genre.length() != 0 )
282
    {
283
        track->setGenre( genre );
284
    }
285
286
    return track;
}