mirror of
https://github.com/transmission/transmission
synced 2025-02-22 14:10:34 +00:00
feat: add torrent-get 'primary-mime-type' to RPC. (#1464)
* feat: add torrent-get 'primary-mime-type' to RPC.
This is a cheap way for RPC clients to know what type of content is in a
torrent. This info can be used to display the torrent, e.g. by using an
icon that corresponds to the mime type.
* use size_t for content byte count
Co-authored-by: Mike Gelfand <mikedld@users.noreply.github.com>
* explicit boolean expressions
Co-authored-by: Mike Gelfand <mikedld@users.noreply.github.com>
* use uint64_t for content byte counts
Co-authored-by: Mike Gelfand <mikedld@users.noreply.github.com>
* avoid unnecessary logic branches
Co-authored-by: Mike Gelfand <mikedld@users.noreply.github.com>
* explicit cast
Co-authored-by: Mike Gelfand <mikedld@users.noreply.github.com>
* refactor: add an autogenerated mime-type.h header
* chore: maybe fix the win32 FTBFS
* chore: add mime-types.[ch] to xcode
* Squashed commit of the following:
commit 4c7153fa48
Author: Mike Gelfand <mikedld@users.noreply.github.com>
Date: Tue Oct 13 03:15:19 2020 +0300
Remove autotools-based build system (#1465)
* Support .git files (e.g. for worktrees, submodules)
* Fix symlinks in source tarball, switch to TXZ, adjust non-release name
* Remove autotools stuff
Co-authored-by: Mike Gelfand <mikedld@users.noreply.github.com>
This commit is contained in:
parent
4c7153fa48
commit
f59118d1fe
19 changed files with 1509 additions and 25 deletions
|
@ -367,6 +367,8 @@
|
|||
C1FEE5791C3223CC00D62832 /* watchdir-kqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = C1FEE5741C3223CC00D62832 /* watchdir-kqueue.c */; };
|
||||
C1FEE57A1C3223CC00D62832 /* watchdir.c in Sources */ = {isa = PBXBuildFile; fileRef = C1FEE5751C3223CC00D62832 /* watchdir.c */; };
|
||||
C1FEE57B1C3223CC00D62832 /* watchdir.h in Headers */ = {isa = PBXBuildFile; fileRef = C1FEE5761C3223CC00D62832 /* watchdir.h */; };
|
||||
CAB35C64252F6F5E00552A55 /* mime-types.h in Headers */ = {isa = PBXBuildFile; fileRef = CAB35C62252F6F5E00552A55 /* mime-types.h */; };
|
||||
CAB35C65252F6F5E00552A55 /* mime-types.c in Sources */ = {isa = PBXBuildFile; fileRef = CAB35C63252F6F5E00552A55 /* mime-types.c */; };
|
||||
D4AF3B2F0C41F7A500D46B6B /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = D4AF3B2D0C41F7A500D46B6B /* list.c */; };
|
||||
D4AF3B300C41F7A600D46B6B /* list.h in Headers */ = {isa = PBXBuildFile; fileRef = D4AF3B2E0C41F7A500D46B6B /* list.h */; };
|
||||
E138A9780C04D88F00C5426C /* ProgressGradients.m in Sources */ = {isa = PBXBuildFile; fileRef = E138A9760C04D88F00C5426C /* ProgressGradients.m */; };
|
||||
|
@ -1018,6 +1020,8 @@
|
|||
C1FEE5741C3223CC00D62832 /* watchdir-kqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "watchdir-kqueue.c"; sourceTree = "<group>"; };
|
||||
C1FEE5751C3223CC00D62832 /* watchdir.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = watchdir.c; sourceTree = "<group>"; };
|
||||
C1FEE5761C3223CC00D62832 /* watchdir.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = watchdir.h; sourceTree = "<group>"; };
|
||||
CAB35C62252F6F5E00552A55 /* mime-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mime-types.h"; sourceTree = "<group>"; };
|
||||
CAB35C63252F6F5E00552A55 /* mime-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mime-types.c"; sourceTree = "<group>"; };
|
||||
D4AF3B2D0C41F7A500D46B6B /* list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = list.c; sourceTree = "<group>"; };
|
||||
D4AF3B2E0C41F7A500D46B6B /* list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = "<group>"; };
|
||||
E138A9750C04D88F00C5426C /* ProgressGradients.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgressGradients.h; sourceTree = "<group>"; };
|
||||
|
@ -1368,6 +1372,8 @@
|
|||
4D1838DC09DEC04A0047D688 /* libtransmission */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CAB35C63252F6F5E00552A55 /* mime-types.c */,
|
||||
CAB35C62252F6F5E00552A55 /* mime-types.h */,
|
||||
C1077A4A183EB29600634C22 /* error.c */,
|
||||
C1077A4B183EB29600634C22 /* error.h */,
|
||||
C1077A4C183EB29600634C22 /* file-posix.c */,
|
||||
|
@ -1866,6 +1872,7 @@
|
|||
A247A443114C701800547DFC /* InfoViewController.h in Headers */,
|
||||
A220EC5C118C8A060022B4BE /* tr-lpd.h in Headers */,
|
||||
A23547E311CD0B090046EAE6 /* cache.h in Headers */,
|
||||
CAB35C64252F6F5E00552A55 /* mime-types.h in Headers */,
|
||||
A284214512DA663E00FBDDBB /* tr-udp.h in Headers */,
|
||||
C1077A4F183EB29600634C22 /* error.h in Headers */,
|
||||
A2679295130E00A000CB7464 /* tr-utp.h in Headers */,
|
||||
|
@ -2173,7 +2180,6 @@
|
|||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 0420;
|
||||
ORGANIZATIONNAME = "The Transmission Project";
|
||||
TargetAttributes = {
|
||||
|
@ -2204,6 +2210,7 @@
|
|||
de,
|
||||
da,
|
||||
"pt-PT",
|
||||
pt_PT,
|
||||
);
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA /* Transmission */;
|
||||
projectDirPath = "";
|
||||
|
@ -2414,6 +2421,7 @@
|
|||
A201527E0D1C270F0081714F /* torrent-ctor.c in Sources */,
|
||||
A2D22A130D65EEE700007D5F /* verify.c in Sources */,
|
||||
4D4ADFC70DA1631500A68297 /* blocklist.c in Sources */,
|
||||
CAB35C65252F6F5E00552A55 /* mime-types.c in Sources */,
|
||||
A29DF8B90DB2544C00D04E5A /* resume.c in Sources */,
|
||||
A2A4E9220DE0F7EB000CE197 /* web.c in Sources */,
|
||||
A2A4EA0E0DE106EB000CE197 /* ConvertUTF.c in Sources */,
|
||||
|
|
|
@ -195,6 +195,7 @@
|
|||
errorString | string | tr_stat
|
||||
eta | number | tr_stat
|
||||
etaIdle | number | tr_stat
|
||||
file-count | number | tr_info
|
||||
files | array (see below) | n/a
|
||||
fileStats | array (see below) | n/a
|
||||
hashString | string | tr_info
|
||||
|
@ -223,6 +224,7 @@
|
|||
pieceCount | number | tr_info
|
||||
pieceSize | number | tr_info
|
||||
priorities | array (see below) | n/a
|
||||
primary-mime-type | string | tr_torrent
|
||||
queuePosition | number | tr_stat
|
||||
rateDownload (B/s) | number | tr_stat
|
||||
rateUpload (B/s) | number | tr_stat
|
||||
|
@ -809,6 +811,9 @@
|
|||
| | yes | torrent-set | new arg "labels"
|
||||
| | yes | torrent-get | new arg "editDate"
|
||||
| | yes | torrent-get | new request arg "format"
|
||||
------+---------+-----------+----------------------+-------------------------------
|
||||
17 | 3.01 | yes | torrent-get | new arg "file-count"
|
||||
| | yes | torrent-get | new arg "primary-mime-type"
|
||||
|
||||
|
||||
5.1. Upcoming Breakage
|
||||
|
|
|
@ -34,6 +34,7 @@ set(PROJECT_FILES
|
|||
magnet.c
|
||||
makemeta.c
|
||||
metainfo.c
|
||||
mime-types.c
|
||||
natpmp.c
|
||||
net.c
|
||||
peer-io.c
|
||||
|
@ -157,6 +158,7 @@ set(${PROJECT_NAME}_PRIVATE_HEADERS
|
|||
list.h
|
||||
magnet.h
|
||||
metainfo.h
|
||||
mime-types.h
|
||||
natpmp_local.h
|
||||
net.h
|
||||
peer-common.h
|
||||
|
|
1213
libtransmission/mime-types.c
Normal file
1213
libtransmission/mime-types.c
Normal file
File diff suppressed because it is too large
Load diff
19
libtransmission/mime-types.h
Normal file
19
libtransmission/mime-types.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* This file Copyright (C) 2020 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MIME_TYPE_SUFFIX_MAXLEN 24
|
||||
#define MIME_TYPE_SUFFIX_COUNT 1201
|
||||
|
||||
struct mime_type_suffix
|
||||
{
|
||||
char const* suffix;
|
||||
char const* mime_type;
|
||||
};
|
||||
|
||||
extern struct mime_type_suffix const mime_type_suffixes[MIME_TYPE_SUFFIX_COUNT];
|
72
libtransmission/mime-types.js
Executable file
72
libtransmission/mime-types.js
Executable file
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const copyright =
|
||||
`/*
|
||||
* This file Copyright (C) ${new Date().getFullYear()} Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*/`;
|
||||
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
|
||||
// https://github.com/jshttp/mime-db
|
||||
// > If you're crazy enough to use this in the browser, you can just grab
|
||||
// > the JSON file using jsDelivr. It is recommended to replace master with
|
||||
// > a release tag as the JSON format may change in the future.
|
||||
//
|
||||
// This script keeps it at `master` to pick up any fixes that may have landed.
|
||||
// If the format changes, we'll just update this script.
|
||||
const url = 'https://cdn.jsdelivr.net/gh/jshttp/mime-db@master/db.json';
|
||||
|
||||
https.get(url, (res) => {
|
||||
res.setEncoding('utf8');
|
||||
const chunks = [];
|
||||
res.on('data', (chunk) => chunks.push(chunk));
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const suffixes = [];
|
||||
const mime_types = JSON.parse(chunks.join(''));
|
||||
for (const [mime_type, info] of Object.entries(mime_types)) {
|
||||
for (const suffix of info?.extensions || []) {
|
||||
suffixes.push([ suffix, mime_type ]);
|
||||
}
|
||||
}
|
||||
|
||||
const max_suffix_len = suffixes
|
||||
.reduce((acc, [suffix]) => Math.max(acc, suffix.length), 0);
|
||||
|
||||
const mime_type_lines = suffixes
|
||||
.map(([suffix, mime_type]) => ` { "${suffix}", "${mime_type}" }`)
|
||||
.sort()
|
||||
.join(',\n');
|
||||
fs.writeFileSync('mime-types.c', `${copyright}
|
||||
|
||||
#include "mime-types.h"
|
||||
|
||||
struct mime_type_suffix const mime_type_suffixes[MIME_TYPE_SUFFIX_COUNT] =
|
||||
{
|
||||
${mime_type_lines}
|
||||
};
|
||||
`);
|
||||
fs.writeFileSync('mime-types.h', `${copyright}
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MIME_TYPE_SUFFIX_MAXLEN ${max_suffix_len}
|
||||
#define MIME_TYPE_SUFFIX_COUNT ${suffixes.length}
|
||||
|
||||
struct mime_type_suffix
|
||||
{
|
||||
char const* suffix;
|
||||
char const* mime_type;
|
||||
};
|
||||
|
||||
extern struct mime_type_suffix const mime_type_suffixes[MIME_TYPE_SUFFIX_COUNT];
|
||||
`);
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -116,6 +116,7 @@ static struct tr_key_struct const my_static[] =
|
|||
Q("etaIdle"),
|
||||
Q("failure reason"),
|
||||
Q("fields"),
|
||||
Q("file-count"),
|
||||
Q("fileStats"),
|
||||
Q("filename"),
|
||||
Q("files"),
|
||||
|
@ -255,6 +256,7 @@ static struct tr_key_struct const my_static[] =
|
|||
Q("port-is-open"),
|
||||
Q("preallocation"),
|
||||
Q("prefetch-enabled"),
|
||||
Q("primary-mime-type"),
|
||||
Q("priorities"),
|
||||
Q("priority"),
|
||||
Q("priority-high"),
|
||||
|
|
|
@ -115,6 +115,7 @@ enum
|
|||
TR_KEY_etaIdle,
|
||||
TR_KEY_failure_reason,
|
||||
TR_KEY_fields,
|
||||
TR_KEY_file_count,
|
||||
TR_KEY_fileStats,
|
||||
TR_KEY_filename,
|
||||
TR_KEY_files,
|
||||
|
@ -254,6 +255,7 @@ enum
|
|||
TR_KEY_port_is_open,
|
||||
TR_KEY_preallocation,
|
||||
TR_KEY_prefetch_enabled,
|
||||
TR_KEY_primary_mime_type,
|
||||
TR_KEY_priorities,
|
||||
TR_KEY_priority,
|
||||
TR_KEY_priority_high,
|
||||
|
|
|
@ -640,6 +640,10 @@ static void initField(tr_torrent* const tor, tr_info const* const inf, tr_stat c
|
|||
tr_variantInitInt(initme, st->eta);
|
||||
break;
|
||||
|
||||
case TR_KEY_file_count:
|
||||
tr_variantInitInt(initme, inf->fileCount);
|
||||
break;
|
||||
|
||||
case TR_KEY_files:
|
||||
tr_variantInitList(initme, inf->fileCount);
|
||||
addFiles(tor, initme);
|
||||
|
@ -779,6 +783,10 @@ static void initField(tr_torrent* const tor, tr_info const* const inf, tr_stat c
|
|||
tr_variantInitInt(initme, inf->pieceSize);
|
||||
break;
|
||||
|
||||
case TR_KEY_primary_mime_type:
|
||||
tr_variantInitStr(initme, tr_torrentPrimaryMimeType(tor), TR_BAD_SIZE);
|
||||
break;
|
||||
|
||||
case TR_KEY_priorities:
|
||||
tr_variantInitList(initme, inf->fileCount);
|
||||
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
|
||||
|
|
|
@ -3378,6 +3378,58 @@ void tr_torrentSetLocation(tr_torrent* tor, char const* location, bool move_from
|
|||
tr_runInEventThread(tor->session, setLocation, data);
|
||||
}
|
||||
|
||||
char const* tr_torrentPrimaryMimeType(tr_torrent const* tor)
|
||||
{
|
||||
struct count
|
||||
{
|
||||
uint64_t length;
|
||||
char const* mime_type;
|
||||
};
|
||||
|
||||
tr_info const* inf = &tor->info;
|
||||
struct count* counts = tr_new0(struct count, inf->fileCount);
|
||||
size_t num_counts = 0;
|
||||
|
||||
for (tr_file const* it = inf->files, * end = it + inf->fileCount; it != end; ++it)
|
||||
{
|
||||
char const* mime_type = tr_get_mime_type_for_filename(it->name);
|
||||
size_t i;
|
||||
for (i = 0; i < num_counts; ++i)
|
||||
{
|
||||
if (counts[i].mime_type == mime_type)
|
||||
{
|
||||
counts[i].length += it->length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == num_counts)
|
||||
{
|
||||
counts[i].mime_type = mime_type;
|
||||
counts[i].length = it->length;
|
||||
++num_counts;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t max_len = 0;
|
||||
char const* mime_type = NULL;
|
||||
for (struct count const* it = counts, *end = it + num_counts; it != end; ++it)
|
||||
{
|
||||
if ((max_len < it->length) && (it->mime_type != NULL))
|
||||
{
|
||||
max_len = it->length;
|
||||
mime_type = it->mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
tr_free(counts);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
// application/octet-stream is the default value for all other cases.
|
||||
// An unknown file type should use this type.
|
||||
return mime_type != NULL ? mime_type : "application/octet-stream";
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
|
|
@ -96,6 +96,10 @@ void tr_torrentSetDateActive(tr_torrent* torrent, time_t activityDate);
|
|||
|
||||
void tr_torrentSetDateDone(tr_torrent* torrent, time_t doneDate);
|
||||
|
||||
/** Return the mime-type (e.g. "audio/x-flac") that matches more of the
|
||||
torrent's content than any other mime-type. */
|
||||
char const* tr_torrentPrimaryMimeType(tr_torrent const* tor);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_VERIFY_NONE,
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "ConvertUTF.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "mime-types.h"
|
||||
#include "net.h"
|
||||
#include "platform.h" /* tr_lockLock() */
|
||||
#include "platform-quota.h" /* tr_device_info_create(), tr_device_info_get_free_space(), tr_device_info_free() */
|
||||
|
@ -2254,3 +2255,38 @@ void tr_net_init(void)
|
|||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// mime-type
|
||||
|
||||
static int compareSuffix(void const* va, void const* vb)
|
||||
{
|
||||
char const* suffix = va;
|
||||
struct mime_type_suffix const* entry = vb;
|
||||
return tr_strcmp0(suffix, entry->suffix);
|
||||
}
|
||||
|
||||
char const* tr_get_mime_type_for_filename(char const* filename)
|
||||
{
|
||||
struct mime_type_suffix const* info = NULL;
|
||||
|
||||
char const* in = strrchr(filename, '.');
|
||||
if ((in != NULL) && (strlen(++in) <= MIME_TYPE_SUFFIX_MAXLEN))
|
||||
{
|
||||
char lowercase_suffix[MIME_TYPE_SUFFIX_MAXLEN + 1];
|
||||
char* out = lowercase_suffix;
|
||||
while (*in != '\0')
|
||||
{
|
||||
*out++ = tolower((unsigned char)*in++);
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
|
||||
info = bsearch(lowercase_suffix,
|
||||
mime_type_suffixes,
|
||||
TR_N_ELEMENTS(mime_type_suffixes),
|
||||
sizeof(*mime_type_suffixes),
|
||||
compareSuffix);
|
||||
}
|
||||
|
||||
return info != NULL ? info->mime_type : NULL;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ char const* tr_strip_positional_args(char const* fmt);
|
|||
|
||||
#define TR_N_ELEMENTS(ary) (sizeof(ary) / sizeof(*(ary)))
|
||||
|
||||
char const* tr_get_mime_type_for_filename(char const* filename);
|
||||
|
||||
/**
|
||||
* @brief Rich Salz's classic implementation of shell-style pattern matching for ?, \, [], and * characters.
|
||||
* @return 1 if the pattern matches, 0 if it doesn't, or -1 if an error occured
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
#include <QFileIconProvider>
|
||||
#include <QFileInfo>
|
||||
#include <QIcon>
|
||||
#include <QMimeDatabase>
|
||||
#include <QObject>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <QPixmapCache>
|
||||
#include <QtWin>
|
||||
#else
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
#endif
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
@ -75,6 +75,58 @@ QIcon IconCache::guessMimeIcon(QString const& filename, QIcon fallback) const
|
|||
return icon;
|
||||
}
|
||||
|
||||
QIcon IconCache::getMimeTypeIcon(QString const& mime_type_name, bool multifile) const
|
||||
{
|
||||
auto& icon = (multifile ? name_to_emblem_icon_ : name_to_icon_)[mime_type_name];
|
||||
|
||||
if (!icon.isNull())
|
||||
{
|
||||
return icon;
|
||||
}
|
||||
|
||||
if (!multifile)
|
||||
{
|
||||
QMimeDatabase mime_db;
|
||||
auto const type = mime_db.mimeTypeForName(mime_type_name);
|
||||
icon = QIcon::fromTheme(type.iconName());
|
||||
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = QIcon::fromTheme(type.genericIconName());
|
||||
}
|
||||
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = file_icon_;
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
auto const mime_icon = getMimeTypeIcon(mime_type_name, false);
|
||||
for (auto const& size : { QSize(24, 24), QSize(32, 32), QSize(48, 48) })
|
||||
{
|
||||
// upper left corner
|
||||
auto const folder_size = size / 2;
|
||||
auto const folder_rect = QRect(QPoint(), folder_size);
|
||||
|
||||
// fullsize
|
||||
auto const mime_size = size;
|
||||
auto const mime_rect = QRect(QPoint(), mime_size);
|
||||
|
||||
// build the icon
|
||||
auto pixmap = QPixmap(size);
|
||||
pixmap.fill(Qt::transparent);
|
||||
QPainter painter(&pixmap);
|
||||
painter.setRenderHints(QPainter::SmoothPixmapTransform);
|
||||
painter.drawPixmap(folder_rect, folder_icon_.pixmap(folder_size));
|
||||
painter.drawPixmap(mime_rect, mime_icon.pixmap(mime_size));
|
||||
icon.addPixmap(pixmap);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
@ -132,11 +184,11 @@ QIcon IconCache::getMimeIcon(QString const& filename) const
|
|||
return {};
|
||||
}
|
||||
|
||||
QIcon& icon = icon_cache_[ext];
|
||||
QIcon& icon = ext_to_icon_[ext];
|
||||
if (icon.isNull()) // cache miss
|
||||
{
|
||||
QMimeDatabase mime_db;
|
||||
QMimeType type = mime_db.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
|
||||
auto const type = mime_db.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = QIcon::fromTheme(type.iconName());
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
QIcon folderIcon() const { return folder_icon_; }
|
||||
QIcon fileIcon() const { return file_icon_; }
|
||||
QIcon guessMimeIcon(QString const& filename, QIcon fallback = {}) const;
|
||||
QIcon getMimeTypeIcon(QString const& mime_type, bool multifile) const;
|
||||
|
||||
protected:
|
||||
IconCache();
|
||||
|
@ -38,11 +39,14 @@ private:
|
|||
QIcon const folder_icon_;
|
||||
QIcon const file_icon_;
|
||||
|
||||
mutable std::unordered_map<QString, QIcon> name_to_icon_;
|
||||
mutable std::unordered_map<QString, QIcon> name_to_emblem_icon_;
|
||||
|
||||
#if defined(_WIN32)
|
||||
void addAssociatedFileIcon(QFileInfo const& file_info, UINT icon_size, QIcon& icon) const;
|
||||
#else
|
||||
mutable std::unordered_map<QString, QIcon> icon_cache_;
|
||||
mutable std::unordered_set<QString> suffixes_;
|
||||
mutable std::unordered_map<QString, QIcon> ext_to_icon_;
|
||||
QIcon getMimeIcon(QString const& filename) const;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -539,11 +539,13 @@ std::vector<std::string_view> const& Session::getKeyNames(TorrentProperties prop
|
|||
if (names.empty())
|
||||
{
|
||||
// unchanging fields needed by the main window
|
||||
static auto constexpr MainInfoKeys = std::array<tr_quark, 6>{
|
||||
static auto constexpr MainInfoKeys = std::array<tr_quark, 8>{
|
||||
TR_KEY_addedDate,
|
||||
TR_KEY_downloadDir,
|
||||
TR_KEY_file_count,
|
||||
TR_KEY_hashString,
|
||||
TR_KEY_name,
|
||||
TR_KEY_primary_mime_type,
|
||||
TR_KEY_totalSize,
|
||||
TR_KEY_trackers,
|
||||
};
|
||||
|
|
|
@ -165,21 +165,7 @@ QIcon Torrent::getMimeTypeIcon() const
|
|||
{
|
||||
if (icon_.isNull())
|
||||
{
|
||||
auto const& files = files_;
|
||||
auto const& icon_cache = IconCache::get();
|
||||
|
||||
if (files.size() > 1)
|
||||
{
|
||||
icon_ = icon_cache.folderIcon();
|
||||
}
|
||||
else if (files.size() == 1)
|
||||
{
|
||||
icon_ = icon_cache.guessMimeIcon(files.at(0).filename, icon_cache.fileIcon());
|
||||
}
|
||||
else
|
||||
{
|
||||
icon_ = icon_cache.guessMimeIcon(name(), icon_cache.folderIcon());
|
||||
}
|
||||
icon_ = IconCache::get().getMimeTypeIcon(primary_mime_type_, file_count_ > 1);
|
||||
}
|
||||
|
||||
return icon_;
|
||||
|
@ -220,6 +206,7 @@ Torrent::fields_t Torrent::update(tr_quark const* keys, tr_variant const* const*
|
|||
HANDLE_KEY(eta, eta, ETA)
|
||||
HANDLE_KEY(fileStats, files, FILES)
|
||||
HANDLE_KEY(files, files, FILES)
|
||||
HANDLE_KEY(file_count, file_count, FILE_COUNT)
|
||||
HANDLE_KEY(hashString, hash, HASH)
|
||||
HANDLE_KEY(haveUnchecked, have_unchecked, HAVE_UNCHECKED)
|
||||
HANDLE_KEY(haveValid, have_verified, HAVE_VERIFIED)
|
||||
|
@ -239,6 +226,7 @@ Torrent::fields_t Torrent::update(tr_quark const* keys, tr_variant const* const*
|
|||
HANDLE_KEY(percentDone, percent_done, PERCENT_DONE)
|
||||
HANDLE_KEY(pieceCount, piece_count, PIECE_COUNT)
|
||||
HANDLE_KEY(pieceSize, piece_size, PIECE_SIZE)
|
||||
HANDLE_KEY(primary_mime_type, primary_mime_type, PRIMARY_MIME_TYPE)
|
||||
HANDLE_KEY(queuePosition, queue_position, QUEUE_POSITION)
|
||||
HANDLE_KEY(rateDownload, download_speed, DOWNLOAD_SPEED)
|
||||
HANDLE_KEY(rateUpload, upload_speed, UPLOAD_SPEED)
|
||||
|
@ -282,7 +270,8 @@ Torrent::fields_t Torrent::update(tr_quark const* keys, tr_variant const* const*
|
|||
{
|
||||
switch (key)
|
||||
{
|
||||
case TR_KEY_name:
|
||||
case TR_KEY_file_count:
|
||||
case TR_KEY_primary_mime_type:
|
||||
{
|
||||
icon_ = {};
|
||||
break;
|
||||
|
@ -290,7 +279,6 @@ Torrent::fields_t Torrent::update(tr_quark const* keys, tr_variant const* const*
|
|||
|
||||
case TR_KEY_files:
|
||||
{
|
||||
icon_ = {};
|
||||
for (int i = 0; i < files_.size(); ++i)
|
||||
{
|
||||
files_[i].index = i;
|
||||
|
|
|
@ -561,6 +561,7 @@ public:
|
|||
ERROR_STRING,
|
||||
ETA,
|
||||
FAILED_EVER,
|
||||
FILE_COUNT,
|
||||
FILES,
|
||||
HASH,
|
||||
HAVE_UNCHECKED,
|
||||
|
@ -582,6 +583,7 @@ public:
|
|||
PERCENT_DONE,
|
||||
PIECE_COUNT,
|
||||
PIECE_SIZE,
|
||||
PRIMARY_MIME_TYPE,
|
||||
QUEUE_POSITION,
|
||||
RECHECK_PROGRESS,
|
||||
SEED_IDLE_LIMIT,
|
||||
|
@ -642,6 +644,7 @@ private:
|
|||
uint64_t desired_available_ = {};
|
||||
uint64_t downloaded_ever_ = {};
|
||||
uint64_t failed_ever_ = {};
|
||||
uint64_t file_count_ = {};
|
||||
uint64_t have_unchecked_ = {};
|
||||
uint64_t have_verified_ = {};
|
||||
uint64_t left_until_done_ = {};
|
||||
|
@ -655,6 +658,7 @@ private:
|
|||
double recheck_progress_ = {};
|
||||
double seed_ratio_limit_ = {};
|
||||
|
||||
QString primary_mime_type_;
|
||||
QString comment_;
|
||||
QString creator_;
|
||||
QString download_dir_;
|
||||
|
|
|
@ -442,3 +442,12 @@ TEST_F(UtilsTest, env)
|
|||
s = makeString(tr_env_get_string(test_key, "c"));
|
||||
EXPECT_EQ("135", s);
|
||||
}
|
||||
|
||||
TEST_F(UtilsTest, mimeTypes)
|
||||
{
|
||||
EXPECT_STREQ("audio/x-flac", tr_get_mime_type_for_filename("music.flac"));
|
||||
EXPECT_STREQ("audio/x-flac", tr_get_mime_type_for_filename("music.FLAC"));
|
||||
EXPECT_STREQ("video/x-msvideo", tr_get_mime_type_for_filename(".avi"));
|
||||
EXPECT_STREQ("video/x-msvideo", tr_get_mime_type_for_filename("/path/to/FILENAME.AVI"));
|
||||
EXPECT_EQ(nullptr, tr_get_mime_type_for_filename("music.ajoijfeisfe"));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue