1 /*
2  * Cantata
3  *
4  * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #ifndef MTPDEVICE_H
25 #define MTPDEVICE_H
26 
27 #include "fsdevice.h"
28 #include "mpd-interface/song.h"
29 #include "config.h"
30 #include "solid-lite/portablemediaplayer.h"
31 #include <libmtp.h>
32 
33 class MusicLibraryItemRoot;
34 class Thread;
35 class QTemporaryFile;
36 
37 class MtpConnection : public QObject
38 {
39     Q_OBJECT
40 public:
41     struct File {
nameFile42         File(const QString &n=QString(), uint64_t s=0, uint32_t i=0) : name(n), size(s), id(i) { }
43         QString name;
44         uint64_t size;
45         uint32_t id;
46     };
47 
48     struct Folder {
49         Folder(const QString &pth=QString(), uint32_t i=0, uint32_t p=0, uint32_t s=0)
pathFolder50             : path(pth)
51             , id(i)
52             , parentId(p)
53             , storageId(s) {
54         }
55         QString path;
56         uint32_t id;
57         uint32_t parentId;
58         uint32_t storageId;
59         QSet<uint32_t> folders;
60         QMap<uint32_t, File> files;
61         QMap<uint32_t, File> covers;
62     };
63 
64     struct Storage : public DeviceStorage {
StorageStorage65         Storage() : id(0), musicFolderId(0) { }
66         uint32_t id;
67         uint32_t musicFolderId;
68         QString musicPath;
69     };
70 
71     MtpConnection(const QString &id, unsigned int bus, unsigned int dev, bool aaSupport);
72     virtual ~MtpConnection();
73 
isConnected()74     bool isConnected() const { return 0!=device; }
75     MusicLibraryItemRoot * takeLibrary();
capacity()76     uint64_t capacity() const { return size; }
usedSpace()77     uint64_t usedSpace() const { return used; }
78     QList<DeviceStorage> getStorageList() const;
79     void emitProgress(int percent);
80     void trackListProgress(int percent);
requestAbort(bool r)81     void requestAbort(bool r) { abortRequested=r; }
abortWasRequested()82     bool abortWasRequested() const { return abortRequested; }
83 
84 public Q_SLOTS:
85     void connectToDevice();
86     void disconnectFromDevice(bool showStatus=true);
87     void updateLibrary(const DeviceOptions &opts);
88     void putSong(const Song &song, bool fixVa, const DeviceOptions &opts, bool overwrite, bool copyCover);
89     void getSong(const Song &song, const QString &dest, bool fixVa, bool copyCover);
90     void delSong(const Song &song);
91     void cleanDirs(const QSet<QString> &dirs);
92     void getCover(const Song &song);
93     void stop();
94 
95 Q_SIGNALS:
96     void statusMessage(const QString &message);
97     void putSongStatus(int, const QString &, bool, bool);
98     void getSongStatus(bool ok, bool copiedCover);
99     void delSongStatus(bool);
100     void cleanDirsStatus(bool ok);
101     void libraryUpdated();
102     void progress(int);
103     void deviceDetails(const QString &serialNumber);
104     void updatePercentage(int);
105     void cover(const Song &s, const QImage &img);
106 
107 private:
108     File getCoverDetils(const Song &s);
109     bool removeFolder(uint32_t folderId);
110     void updateFilesAndFolders();
111     void listFolder(uint32_t storage, uint32_t parentDir, Folder *f=0);
112     void updateStorage();
113     Storage & getStorage(const QString &volumeIdentifier);
114     Storage & getStorage(uint32_t id);
115     uint32_t createFolder(const QString &name, const QString &fullPath, uint32_t parentId, uint32_t storageId);
116     uint32_t getFolder(const QString &path, uint32_t storageId);
117     QString getPath(uint32_t folderId);
118     uint32_t checkFolderStructure(const QStringList &dirs, Storage &store);
119     void setMusicFolder(Storage &store);
120     #ifdef MTP_CLEAN_ALBUMS
121     void updateAlbums();
122     LIBMTP_album_t * getAlbum(const Song &song);
123     #endif
124     void destroyData();
125 
126 private:
127     Thread *thread;
128     LIBMTP_mtpdevice_t *device;
129     #ifdef MTP_CLEAN_ALBUMS
130     LIBMTP_album_t *albums;
131     #endif
132     QMap<uint32_t, Folder> folderMap;
133     MusicLibraryItemRoot *library;
134     uint32_t defaultMusicFolder;
135     QList<Storage> storage;
136     uint64_t size;
137     uint64_t used;
138     int lastListPercent;
139     bool abortRequested;
140     unsigned int busNum;
141     unsigned int devNum;
142     bool supprtAlbumArtistTag;
143 };
144 
145 class MtpDevice : public Device
146 {
147     Q_OBJECT
148 
149 public:
150     MtpDevice(MusicLibraryModel *m, Solid::Device &dev, unsigned int busNum, unsigned int devNum);
151     virtual ~MtpDevice();
152 
153     bool isConnected() const;
isRefreshing()154     bool isRefreshing() const { return mtpUpdating; }
155     void stop();
156     void configure(QWidget *parent);
path()157     QString path() const { return QString(); } // audioFolder; }
158     void addSong(const Song &s, bool overwrite, bool copyCover);
159     void copySongTo(const Song &s, const QString &musicPath, bool overwrite, bool copyCover);
160     void removeSong(const Song &s);
161     void cleanDirs(const QSet<QString> &dirs);
162     Covers::Image requestCover(const Song &song);
163     double usedCapacity();
164     QString capacityString();
165     qint64 freeSpace();
devType()166     DevType devType() const { return Mtp; }
167     void saveOptions();
abortJob()168     void abortJob() { requestAbort(true); }
169 
170 Q_SIGNALS:
171     // These are for talking to connection thread...
172     void updateLibrary(const DeviceOptions &opts);
173     void putSong(const Song &song, bool fixVa, const DeviceOptions &opts, bool overwrite, bool copyCover);
174     void getSong(const Song &song, const QString &dest, bool fixVa, bool copyCover);
175     void delSong(const Song &song);
176     void cleanMusicDirs(const QSet<QString> &dirs);
177     void getCover(const Song &s);
178     void cover(const Song &s, const QImage &img);
179 
180 private Q_SLOTS:
181     void deviceDetails(const QString &s);
182     void libraryUpdated();
183     void rescan(bool full=true);
184     void putSongStatus(int status, const QString &file, bool fixedVa, bool copiedCover);
185     void transcodeSongResult(int status);
186     void transcodePercent(int percent);
187     void emitProgress(int);
188     void getSongStatus(bool ok, bool copiedCover);
189     void delSongStatus(bool ok);
190     void cleanDirsStatus(bool ok);
191     void saveProperties(const QString &newPath, const DeviceOptions &opts);
192     void saveProperties();
193 
194 private:
195     void deleteTemp();
requestAbort(bool r)196     void requestAbort(bool r) { if (connection) connection->requestAbort(r); jobAbortRequested=r; }
197 
198 private:
199     Solid::PortableMediaPlayer *pmp;
200     MtpConnection *connection;
201     QTemporaryFile *tempFile;
202     Song currentSong;
203     bool mtpUpdating;
204     QString serial;
205     QString defaultName;
206     friend class MtpConnection;
207 };
208 
209 #endif
210