~mediascanner-team/mediascanner2/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * Copyright (C) 2013 Canonical, Ltd.
 *
 * Authors:
 *    Jussi Pakkanen <jussi.pakkanen@canonical.com>
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of version 3 of the GNU General Public License as published
 * by the Free Software Foundation.
 *
 * This library 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Scanner.hh"
#include "../extractor/DetectedFile.hh"
#include "../extractor/MetadataExtractor.hh"
#include "../mediascanner/internal/utils.hh"
#include <dirent.h>
#include <sys/stat.h>
#include<cstdio>
#include<cstdlib>
#include<memory>
#include<cassert>

using namespace std;

namespace mediascanner {

struct Scanner::Private {
    Private(MetadataExtractor *extractor_, const std::string &root, const MediaType type_);

    string curdir;
    vector<string> dirs;
    unique_ptr<struct dirent, void(*)(void*)> entry;
    unique_ptr<DIR, int(*)(DIR*)> dir;
    MediaType type;
    MetadataExtractor *extractor;
    struct dirent *de;
};

Scanner::Private::Private(MetadataExtractor *extractor, const std::string &root, const MediaType type) :
        entry((dirent*)malloc(sizeof(dirent) + NAME_MAX + 1), free),
        dir(nullptr, closedir),
        type(type),
        extractor(extractor),
        de(nullptr)
{
    dirs.push_back(root);
}

Scanner::Scanner(MetadataExtractor *extractor, const std::string &root, const MediaType type) :
    p(new Scanner::Private(extractor, root, type)) {
}

Scanner::~Scanner() {
    delete p;
}


DetectedFile Scanner::next() {
begin:
    while(!p->dir) {
        if(p->dirs.empty()) {
            throw StopIteration();
        }
        p->curdir = p->dirs.back();
        p->dirs.pop_back();
        unique_ptr<DIR, int(*)(DIR*)> tmp(opendir(p->curdir.c_str()), closedir);
        p->dir = move(tmp);
        if(is_rootlike(p->curdir)) {
            fprintf(stderr, "Directory %s looks like a top level root directory, skipping it (%s).\n",
                    p->curdir.c_str(), __PRETTY_FUNCTION__);
            p->dir.reset();
            continue;
        }
        if(has_scanblock(p->curdir)) {
            fprintf(stderr, "Directory %s has a scan block file, skipping it.\n",
                    p->curdir.c_str());
            p->dir.reset();
            continue;
        }
        printf("In subdir %s\n", p->curdir.c_str());
    }

    while(readdir_r(p->dir.get(), p->entry.get(), &p->de) == 0 && p->de ) {
        struct stat statbuf;
        string fname = p->entry.get()->d_name;
        if(fname[0] == '.') // Ignore hidden files and dirs.
            continue;
        string fullpath = p->curdir + "/" + fname;
        lstat(fullpath.c_str(), &statbuf);
        if(S_ISREG(statbuf.st_mode)) {
            try {
                DetectedFile d = p->extractor->detect(fullpath);
                if (p->type == AllMedia || d.type == p->type) {
                    return d;
                }
            } catch (const exception &e) {
                /* Ignore non-media files */
            }
        } else if(S_ISDIR(statbuf.st_mode)) {
            p->dirs.push_back(fullpath);
        }
    }

    // Nothing left in this directory so on to the next.
    assert(!p->de);
    p->dir.reset();
    // This should be just return next(s) but we can't guarantee
    // that GCC can optimize away the tail recursion so we do this
    // instead. Using goto instead of wrapping the whole function body in
    // a while(true) to avoid the extra indentation.
    goto begin;
}

}