T800G / CBXShell

Thumbnail preview for ZIP and RAR image archives
121 stars 21 forks source link

Adding ePub support #1

Open Serized opened 7 years ago

Serized commented 7 years ago

Hi!

Thanks for this great software. I'm currently using it on windows 10 to display thumbnails for epub files. Would you consider adding an option to display epub thumbnails directly in the UI if it's not too complicated?

Thanks!

L0garithmic commented 4 years ago

To anyone wondering, this is how you do the epub thumbnails https://wiki.mobileread.com/wiki/Thumbnails

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.epub\shellex]

[HKEY_CLASSES_ROOT\.epub\shellex\{00021500-0000-0000-C000-000000000046}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

[HKEY_CLASSES_ROOT\.epub\shellex\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

Just save this as something.reg, run it and you should have thumbnails. If you do not see the thumbnails right away, toggle the "Sort Images Alphabetically", that made it work for me, no reboot.

samscudder commented 4 years ago

I wrote this a while back to correctly handle epubs, displaying the right cover image even if it's not the first file in the epub (zip).

In cbxArchive.h:

#define CBXTYPE_EPUB 5

at line 45.

Add this to CCBXArchive::OnExtract function, as the first casein the switch (has to be before the ZIP and CBZ, so it falls back in case of a bad EPUB file) :

...
        switch (m_cbxType)
        {
        case CBXTYPE_EPUB:
        {
            CUnzip _z;
            if (!_z.Open(m_cbxFile)) return E_FAIL;
            j = _z.GetItemCount();
            if (j == 0) return E_FAIL;

            size_t posStart, posEnd;

            CString prevname;//helper vars
            int thumbindex = -1;

            USES_CONVERSION;

            std::string xmlContent, rootpath, rootfile, coverfile;

            // Find 
            for (i = 0; i < j; i++)
            {
                if (!_z.GetItem(i)) break;
                if (_z.ItemIsDirectory() || (_z.GetItemUnpackedSize() > CBXMEM_MAXBUFFER_SIZE)) continue;

                std::string name = T2A(_z.GetItemName());

                if (_stricmp(name.c_str(), "META-INF/container.xml") == 0) {
                    // Extract container.xml and retrieve rootfile location

                    HGLOBAL hGContainer = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (SIZE_T)_z.GetItemUnpackedSize());
                    if (hGContainer)
                    {
                        bool b = false;
                        LPVOID pBuf = ::GlobalLock(hGContainer);
                        if (pBuf)
                            b = _z.UnzipItemToMembuffer(i, pBuf, _z.GetItemUnpackedSize());

                        if (::GlobalUnlock(hGContainer) == 0 && GetLastError() == NO_ERROR)
                        {
                            if (b)
                            {
                                xmlContent = (char *)pBuf;

                                posStart = xmlContent.find("rootfile ");

                                if (posStart == std::string::npos) {
                                    break;
                                }

                                posStart = xmlContent.find("full-path=\"", posStart);

                                if (posStart == std::string::npos) {
                                    break;
                                }

                                posStart += 11;
                                posEnd = xmlContent.find("\"", posStart);

                                rootfile = xmlContent.substr(posStart, posEnd - posStart);

                                break;
                            }
                        }
                        //GlobalFree(hGContainer);//autofreed
                    }
                }
            }

            if (rootfile.length() > 0) {

                posStart = rootfile.find('/');
                if (posStart != std::string::npos) {
                    rootpath = rootfile.substr(0, posStart + 1);
                }

                for (i = 0; i < j; i++)
                {
                    if (!_z.GetItem(i)) break;
                    if (_z.ItemIsDirectory() || (_z.GetItemUnpackedSize() > CBXMEM_MAXBUFFER_SIZE)) continue;

                    std::string name;

                    name = CT2A(_z.GetItemName());

                    if (name.compare(rootfile) == 0) {
                        HGLOBAL hGContainer = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (SIZE_T)_z.GetItemUnpackedSize());
                        if (hGContainer)
                        {
                            bool b = false;
                            LPVOID pBuf = ::GlobalLock(hGContainer);
                            if (pBuf)
                                b = _z.UnzipItemToMembuffer(i, pBuf, _z.GetItemUnpackedSize());

                            if (::GlobalUnlock(hGContainer) == 0 && GetLastError() == NO_ERROR)
                            {
                                if (b)
                                {
                                    // Find meta tag for cover

                                    std::string xmlContent, coverTag, coverId, itemTag;

                                    xmlContent = (char *)pBuf;

                                    posStart = xmlContent.find("name=\"cover\"");

                                    if (posStart == std::string::npos) {
                                        break;
                                    }

                                    posStart = xmlContent.find_last_of("<", posStart);
                                    posEnd = xmlContent.find(">", posStart);

                                    coverTag = xmlContent.substr(posStart, posEnd - posStart + 1);

                                    // Find cover item id

                                    posStart = coverTag.find("content=\"");

                                    if (posStart == std::string::npos) {
                                        break;
                                    }

                                    posStart += 9;
                                    posEnd = coverTag.find("\"", posStart);

                                    coverId = coverTag.substr(posStart, posEnd - posStart);

                                    // Find item tag in original opf file contents

                                    posStart = xmlContent.find("id=\"" + coverId);

                                    if (posStart == std::string::npos) {
                                        break;
                                    }

                                    posStart = xmlContent.find_last_of("<", posStart);
                                    posEnd = xmlContent.find(">", posStart);

                                    itemTag = xmlContent.substr(posStart, posEnd - posStart + 1);

                                    // Find cover path in item tag

                                    posStart = itemTag.find("href=\"");

                                    if (posStart == std::string::npos) {
                                        break;
                                    }

                                    posStart += 6;
                                    posEnd = itemTag.find("\"", posStart);

                                    if (posEnd == std::string::npos) {
                                        break;
                                    }

                                    coverfile = rootpath + itemTag.substr(posStart, posEnd - posStart);
                                    ReplaceStringInPlace(coverfile, "%20", " ");
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            if (coverfile.length() > 0) {

                for (i = 0; i < j; i++) {
                    if (!_z.GetItem(i)) break;
                    if (_z.ItemIsDirectory() || (_z.GetItemUnpackedSize() > CBXMEM_MAXBUFFER_SIZE)) continue;
                    if ((_z.GetItemPackedSize() == 0) || (_z.GetItemUnpackedSize() == 0)) continue;

                    if (_stricmp(T2A(_z.GetItemName()), coverfile.c_str()) == 0 && IsImage(_z.GetItemName()))
                    {
                        if (thumbindex < 0) thumbindex = i;// assign thumbindex if already sorted

                        if (!m_bSort) break;//if NoSort

                        if (prevname.IsEmpty()) prevname = _z.GetItemName();//can't compare empty string
                        //take only first alphabetical name
                        if (-1 == StrCmpLogicalW(_z.GetItemName(), prevname))
                        {
                            thumbindex = i;
                            prevname = _z.GetItemName();
                        }
                    }
                }//for loop

                if (thumbindex < 0) return E_FAIL;
                //go to thumb index
                if (!_z.GetItem(thumbindex)) return E_FAIL;

                //create thumb          //GHND
                HGLOBAL hG = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (SIZE_T)_z.GetItemUnpackedSize());
                if (hG)
                {
                    bool b = false;
                    LPVOID pBuf = ::GlobalLock(hG);
                    if (pBuf)
                        b = _z.UnzipItemToMembuffer(thumbindex, pBuf, _z.GetItemUnpackedSize());

                    if (::GlobalUnlock(hG) == 0 && GetLastError() == NO_ERROR)
                    {
                        if (b)
                        {
                            IStream* pIs = NULL;
                            if (S_OK == CreateStreamOnHGlobal(hG, TRUE, (LPSTREAM*)&pIs))//autofree hG
                            {
                                *phBmpThumbnail = ThumbnailFromIStream(pIs, &m_thumbSize);
                                pIs->Release();
                                pIs = NULL;
                            }
                        }
                    }
                }
                //GlobalFree(hG);//autofreed
                return ((*phBmpThumbnail) ? S_OK : E_FAIL);
            }

            // something wrong with the epub, try falling back on first image in zip
        }
        case CBXTYPE_ZIP:
        case CBXTYPE_CBZ:
            {
...

In the GetInfoTip function add this case at the end (the last lines already exist).

        case CBXTYPE_EPUB:
            if (_fs == 0) tip = _T("EPUB File\nSize: 0 bytes");
            else
            {
                std::string title = GetEpubTitle(m_cbxFile);
                ATL::CAtlStringW titleW = CA2W(title.c_str());

                if (title.length() > 0)
                    tip.Format(_T("EPUB File\n%s\nSize: %s"),
                        titleW, StrFormatByteSize64(_fs, _tf, 16));
                else tip.Format(_T("EPUB File\nSize: %s"), StrFormatByteSize64(_fs, _tf, 16));
            }
        break;

        default: ATLTRACE("IQueryInfo::GetInfoTip : CBXTYPE_NONE\n");return E_FAIL;
        }
...

In GetCBXType add:

        //by popular demand
        if (StrEqual(szExt, _T(".epub"))) return CBXTYPE_EPUB;

I think that's all I did. Could probably write it a bit better today, given time.

Feel free to add to the project if you want.

reddevilrulez commented 4 years ago

To anyone wondering, this is how you do the epub thumbnails https://wiki.mobileread.com/wiki/Thumbnails

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.epub\shellex]

[HKEY_CLASSES_ROOT\.epub\shellex\{00021500-0000-0000-C000-000000000046}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

[HKEY_CLASSES_ROOT\.epub\shellex\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

Just save this as something.reg, run it and you should have thumbnails. If you do not see the thumbnails right away, toggle the "Sort Images Alphabetically", that made it work for me, no reboot.

Tried works, but shows a small pdf banner on the right bottom of the epub file thumbnail in explorer, because of sumatra reader association. Changed the default app to calibre viewer and got a generic icon at the bootm of thumbnail. Thanks for the tip.

L0garithmic commented 4 years ago

To anyone wondering, this is how you do the epub thumbnails https://wiki.mobileread.com/wiki/Thumbnails

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.epub\shellex]

[HKEY_CLASSES_ROOT\.epub\shellex\{00021500-0000-0000-C000-000000000046}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

[HKEY_CLASSES_ROOT\.epub\shellex\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

Just save this as something.reg, run it and you should have thumbnails. If you do not see the thumbnails right away, toggle the "Sort Images Alphabetically", that made it work for me, no reboot.

Tried works, but shows a small pdf banner on the right bottom of the epub file thumbnail in explorer, because of sumatra reader association. Changed the default app to calibre viewer and got a generic icon at the bootm of thumbnail. Thanks for the tip.

Working on having a new version of CBXShell made with native ePub compatibility and better image parsing. Give me a few days.

reddevilrulez commented 4 years ago

To anyone wondering, this is how you do the epub thumbnails https://wiki.mobileread.com/wiki/Thumbnails

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.epub\shellex]

[HKEY_CLASSES_ROOT\.epub\shellex\{00021500-0000-0000-C000-000000000046}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

[HKEY_CLASSES_ROOT\.epub\shellex\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}]
@="{9E6ECB90-5A61-42BD-B851-D3297D9C7F39}"

Just save this as something.reg, run it and you should have thumbnails. If you do not see the thumbnails right away, toggle the "Sort Images Alphabetically", that made it work for me, no reboot.

Tried works, but shows a small pdf banner on the right bottom of the epub file thumbnail in explorer, because of sumatra reader association. Changed the default app to calibre viewer and got a generic icon at the bootm of thumbnail. Thanks for the tip.

Working on having a new version of CBXShell made with native ePub compatibility and better image parsing. Give me a few days.

Cool, Ok. Please consider adding WebP preview support as well if possible. https://github.com/T800G/CBXShell/issues/5

L0garithmic commented 4 years ago

Do you have any idea how to do this? Any code example snippets? My coder is currently working on it as we speak. Can you write up a simple example of what you are talking about and paste it here, I will send him the link!

reddevilrulez commented 4 years ago

Do you have any idea how to do this? Any code example snippets? My coder is currently working on it as we speak. Can you write up a simple example of what you are talking about and paste it here, I will send him the link!

In cbxArchive.h: have to add in the line in bold as below:

BOOL IsImage(LPCTSTR szFile) { LPWSTR _e=PathFindExtension(szFile); if (StrEqual(_e, _T(".bmp"))) return TRUE; if (StrEqual(_e, _T(".ico"))) return TRUE; if (StrEqual(_e, _T(".gif"))) return TRUE; if (StrEqual(_e, _T(".jpg"))) return TRUE; if (StrEqual(_e, _T(".jpe"))) return TRUE; if (StrEqual(_e, _T(".jfif"))) return TRUE; if (StrEqual(_e, _T(".jpeg"))) return TRUE; if (StrEqual(_e, _T(".png"))) return TRUE; if (StrEqual(_e, _T(".tif"))) return TRUE; if (StrEqual(_e, _T(".tiff"))) return TRUE; if (StrEqual(_e, _T(".webp"))) return TRUE; return FALSE; }

L0garithmic commented 4 years ago

Released a new program that fully supports EPUB files. We could not get WebP to work, but if someone figures out how to, they are more then welcome to suggest a change!

https://github.com/dark-knight404/DarkThumbs

MathmaGGic commented 2 years ago

I'd like add GGB extension but I do not know how to compile the source code can somebody help me for this?