Kindle, chmura i okładki

6 minut(y)

Aktualnie oprogramowanie czytników Kindle umożliwia wyświetlanie w bibliotece okładek książek i audiobooków. Działa to bezbłędnie z książkami ze sklepu Amazona oraz audiobookami z Audible. Dla tych treści, od niedawna okładka wyświetla się także na ekranie blokady (wygaszaczu ekranu).

Niestety powyższe funkcje nie działają tak dobrze dla książek spoza ekosystemu Amazona, szczególnie tych wysyłanych przez chmurę. Możliwe jest łatwe stworzenie e-booka i wgranie go po kablu, takie rozwiązanie ma jednak liczne ograniczenia i nie będę go poważnie rozważał. Skupię się na treściach przesyłanych przez chmurę Amazona.

Dawno, dawno temu…

W czasach kiedy Amazon sprzedawał książki w formacie AZW, czyli lekko zmodyfikowane pliki PRC/MOBI, przesłanie własnych książek przez chmurę skutkowało otrzymaniem na czytniku danego e-booka, który możliwościami nie odbiegał od treści ze sklepu Amazona. Wraz z wprowadzeniem do powszechnego obiegu formatu AZW3 (KF8) sytuacja stopniowo zaczęła ulegać zmianie. A po wprowadzeniu formatu AZW8 (KFX) „postęp” w dyskryminowaniu treści spoza sklepu Amazona wszedł na kolejny poziom.

Pierwsze bitwy

Wraz z wprowadzeniem oprogramowania w wersji 5, Amazon postanowił, że treści przesyłane via e-mail dostaną „łatkę” osobistych dokumentów, a nie książek. I dalej – osobiste dokumenty nie dostaną możliwości wyświetlania okładek.

Na taki stan rzeczy zareagował Robert Błaut, znany też jako quiris, który przygotował narzędzie ExtractCoverThumbs, które pozwalało na wygenerowanie brakujących okładek.

Podstawową wadą ECT była konieczność podłączenia czytnika do komputera co jakiś czas, ale to drobiazg. Genialna programistka Becky przygotowała zestaw narzędzi o nazwie EKT, który korzystając z pomysłu Roberta, ale przy całkowicie innej implementacji m.in. robi to samo, tylko działa z poziomu czytnika. Dzięki temu pozwala uzyskać okładki bez konieczności podłączania czytnika do komputera.

Kontrofensywa Amazona

Wraz z wydaniem oprogramowania o numerze 5.6.5 Amazon postanowił ukrócić możliwości oferowane przez powyższe narzędzia. Robert ulepszył swoje narzędzie, tak by zmieniało w metadanych cdeType z „PDOC” na „EBOK”. W odpowiedzi Amazon wydał wersję 5.8.5, która „poradziła sobie z tymi praktykami”. Od tamtego momentu, system nie tylko sam nie tworzy okładki dla naszych książek przesyłanych przez chmurę, ale dodatkowo stygmatyzuje pliki PDOC, tak by po ręcznym dodaniu okładki, czytnik jej nie wyświetlił.

Oficjalne narzędzie Amazona do tworzenia plików MOBI – Kindle Previewer (a także kindlegen), nie wypełnia pola 501 w metadanych, a chmura widząc brak tego pola, wstawia tam ciąg „PDOC”. W ten sposób uzyskujemy efekt zamierzony przez Amazona – ładne okładki tylko dla książek z Kindle Store.

Moja odsiecz…

Robert odpuścił, a raczej stracił możliwość dalszego zwalczania tej brzydkiej zagrywki Amazona. Nie pogodziłem się z takim obrotem spraw i postanowiłem naprawić ten zamierzony i precyzyjnie wdrożony błąd Amazona. Oczywiście bez roota ani rusz, ale jeśli ktoś zadbał o jailbreak odpowiednio wcześniej, to ma szanse powalczyć.

Na początek zlokalizowałem klasę CoverArtServiceImpl.class. A w niej taką instrukcję:

if (s.charAt(0) == '*' || "PDOC".equals(anObject)) {
  CoverArtServiceImpl.LOG.b(CoverArtServiceImpl.ezL, new String[] { "nonAmazonContent", s }, (String)null);
  return null;
}

Nieco dalej w tej samej klasie znalazłem taką instrukcję:

if ((catalogItem.getCDEKey() == null || catalogItem.getCDEKey().charAt(0) != '*') && !"PDOC".equals(catalogItem.getCDEType())) {
  if (!"PSNL".equals(catalogItem.getCDEType())) {
    final String a3 = this.a(catalogItem.getCDEKey(), catalogItem.getCDEType(), null, b, false);
    if (a3 != null) {
      final String thumbnailLocation = catalogItem.getThumbnailLocation();
      if (thumbnailLocation == null || !thumbnailLocation.equals(a3)) {
          CoverArtServiceImpl.mea.put(a3, catalogItem);
      }
    }
  }
}

W obu przypadkach ciąg „PDOC” uznałem za zbędny i postanowiłem się go pozbyć. Tutaj mała uwaga – jakby ktoś planował podobne kroki, to lepiej niech przygotuje się na ewentualny brick czytnika. Ta klasa jest w jednym z kluczowych archiwów JAR i nie można sobie w niej bezkarnie grzebać do woli. Wspomnę tylko, że przy wersji FW 5.9, w miejsce napisów „PDOC” wstawiłem ciąg „JPEG” lub „.JPG” i wszystko działało jak należy, matomiast kiedy zrobiłem to samo w wersji 5.13.6, to zbrickowałem czytnik. Podobnie kiedyś pomyślałem, że skoro to 4 znaki, to wstawię tam „NULL” – to nie był dobry pomysł. W końcu wstawiłem tam właściwy ciąg znaków, który nie powoduje bricka, a co najważniejsze…

…po tej drobnej zmianie EKT (a także ECT) generują okładki, które są widoczne w bibliotece. Także w przypadku plików PDOC.

Na chwilę obecną bitwa zakończona zwycięstwem – być może są subtelniejsze metody, ale to był „pierwszy strzał” i choć dotyka wrażliwych części systemu, to dzięki niemu mam tylko polskie książki (poza jedną testową od Amazona), wszystkie wgrane na czytnik przez chmurę i każda ma okładkę widoczną w bibliotece.

Kolejne pole bitwy – wygaszacz

Wraz z aktualizacją 5.13.6 Amazon wprowadził nową funkcję – wyświetlanie okładki ostatnio otworzonej książki lub audiobooka na wygaszaczu, zamiast domyślnych ołówków i kałamarzy. Niestety, jak zwykle, ta funkcja działa bardzo dobrze jedynie z książkami ze sklepu Amazona i audiobookami z Audible.

Analizowałem oprogramowanie w wersji 5.13.6, czyli najnowsze dla Kindle Voyage. Zerknąłem też pobieżnie na kod z wersji 5.13.7 (dla Kindle Oasis) i zauważyłem, że różni się w kilku miejscach. Użytkownicy nowych czytników Kindle z oprogramowaniem 5.13.7 dostarczają sprzecznych doniesień – u jednych okładki na wygaszaczu są przy każdej książce, u innych przy niektórych, aż wreszcie są też osoby, które okładek na wygaszaczu nie zobaczyły przy żadnej książce spoza sklepu Amazona.

Pierwszą moją myślą było spróbowanie manewru podobnego do tego, który dotyczy okładek w bibliotece. Przeszukałem kod i znalazłem tam taką funkcję:

public static boolean cf(final Book book) {
  if (book != null) {
    final BookMetadata ms = book.ms();
    final String cdeType = ms.getCdeType();
    if (ms != null && !StringUtils.qs(ms.getTitle()) && !StringUtils.qs(ms.nd()) && book.my() != IBook.BookType.INVALID_TYPE && book.my() != IBook.BookType.PDF && book.my() != IBook.BookType.TOPAZ && ms.getContentType() != IBookMetadata.ContentType.BOOK_TYPE_DICTIONARY.getType() && ms.getContentType() != IBookMetadata.ContentType.BOOK_TYPE_NEWSPAPER.getType() && ms.getContentType() != IBookMetadata.ContentType.BOOK_TYPE_FEED.getType() && !cdeType.equals("PDOC")) {
      return true;
    }
  }
  return false;
}

Czyli okładka nie dla PDF-ów, plików TOPAZ, DICTIONARY (nie dotyczy słowników z których korzystamy w czasie lektury), NEWSPAPER, FEED i… „PDOC”.

Jeśli ktoś w tym momencie pomyślał, że „wysadzenie z siodła” tutaj ciągu „PDOC” załatwi sprawę, to niestety tak nie jest.

Popatrzyłem dalej na kod i znalazłem np. taki fragment:

public String getDisplayStyle() {
  return ("AUDI".equals(this.mType) || "EBOK".equals(this.mType)) ? "covers" : null;
}

To jak – dopiszemy tutaj "PDOC".equals(this.mType) i będzie dobrze? Otóż nic tutaj nie dopiszemy – nie mamy pełnych źródeł, więc nie możemy dowolnie dodawać kodu, tylko jedynie podmieniać fragmenty.

To może w miejsce „AUDI” wstawimy „PDOC”? Przede wszystkim to zepsucie domyślnej funkcji czytnika, choć w przypadku Voyage, gdzie audiobooki są niedostępne, to żadna strata, to taka podmiana nie rozwiązuje problemu.

W jeszcze innej klasie istnieje taka instrukcja:

if (this.eAn != null && this.eAq != null && this.eAq.equals(cd) && FileUtils.fileExists(this.eAs) && (!"EBSP".equals(this.eAn.getCdeType()) || !"EBOK".contentEquals(this.ce(book)))) {
  BookCoverHandler.LOG.info("Book cover model is not executed as the same book is opened");
  return;
}

Oczywiście wymiana tutaj „EBSP” na „PDOC” (znowu nie możemy sobie dopisać kodu), poskuktuje okaleczeniem domyślnej funkcji – tutaj na wygaszaczu nie zobaczymy próbek książek ze sklepu Amazona. Oczywiście nie sprawi to, że osobiste dokumenty zaczną wyświetlać okładkę na ekranie blokady.

Nie będę już przytaczał kolejnych znalezionych fragmentów odnoszących się do okładek na wygaszaczu. Podsumuję to tak: mechanizm dyskryminowania książek spoza sklepu Amazona jest rozbity na kilka klas, a kod miejscami jest tak skonstruowany, że nie da się bezpiecznie dodać wyświetlania okładek PDOC–ów na wygaszaczu, bez poświęcenia okładek dla próbek i audiobooków.

Na chwilę obecną, pomaga zmiana w metadanych książki cdeType z „PDOC” na „EBOK”. Po tej prostej operacji, którą wykonuję bezpośrednio na czytniku, okładka na wygaszaczu pojawia się bezproblemowo. Odbywa się to jednak kosztem synchronizacji e-booka pomiędzy różnymi urządzeniami. Czytam tylko na jednym czytniku, więc niespecjalnie mi to przeszkadza. Jeśli później, już po ukazaniu się okładki otworzonej książki na wygaszaczu, przywrócę „PDOC” w metadanych, to okładka nie zniknie – do czasu otworzenia innej książki.

Rozwiązanie średnio mnie zadowala – to w końcu modyfikowanie plików z książkami i poświęcenie synchronizacji. Widząc jednak, że w tej kwestii oprogramowanie różne o jeden numerek na trzeciej pozycji (5.13.7), różni się znacząco i nie mając pewności, że Voyage został całkowicie porzucony, ze znalezieniem bardziej eleganckiego rozwiązania wstrzymam się do wersji 5.14 lub 6.0. W każdym razie bitwa nie jest skończona. Być może interwencja nie będzie potrzebna, bo Amazon dopuści wyświetlanie okładek na wygaszaczu dla PDOC-ów.

Komentarze (2)

  sylwek

Czemu ty nie podajesz, jaki ciąg znaków tam wstawiłeś, co też bym sobie wstawił, albo ten EKT, żebyś wstawił release albo instrukcję, jak uruchomić na czytniku?

Poza tym przyjrzałem się bardziej twoim repo (github athamebook) i ty dokumentujesz kod jak *, ani komentarzy, ani jakiegoś readme, wszystko na zasadzie „wiem, ale nie powiem”.

Odpowiedz




  Athame

Świadomie założyłem, że jak ktoś dotarł do tego etapu, to będzie widział co tam można wpisać, a czego nie wolno. Osobiście wstawiłem jakiś istniejący, a niewykorzystywany przeze mnie ciąg cdeType(). Działają różne, a u mnie jest to prawdopodobnie „PSNL” (a przynajmniej był przez jakiś czas i działał).

EKT jest jakie jest - nie używam JB, a tym bardziej MRPI, więc na własny użytek nie przygotowywałem paczki instalacyjnej. Moi znajomi używają roota ode mnie i tam zupełnie inaczej wgrywamy mody.

Github athamebook jest moim prywatnym, gdzie kod daleko odbiega od uporzadkowanego i nie jest przygotowywany dla powszechnego użytku.

Odpowiedz




Dodaj komentarz