calibre
Author: Kovid Goyal
Date: 2024-05-03T03:02:21+00:00
Description: Comprehensive documentation for calibre

Gezinti

- modülleri
- sonraki |
- Başla »
- calibre Kullanım Kılavuzu

calibre Kullanım Kılavuzu
calibre bir e-kitap kitaplık yöneticisidir. E-kitapları büyük e-kitap biçimlerinin çoğunda görüntüleyebilir, dönüştürebilir ve kataloglayabilir. Ayrıca birçok e-kitap okuyucu cihazıyla da konuşabilir. İnternete gidebilir ve kitaplarınız için üst veriler alabilir. Kolayca okumak için gazeteleri indirebilir ve e-kitaplara dönüştürebilir. Linux, Windows ve macOS’ta çalışan çapraz platformdur.

Calibre’yi yeni başlattınız. Şu an ne yapıyorsun? Calibre e-kitaplarınızla herhangi bir şey yapmadan önce, ilk önce onlar hakkında bilgi sahibi olmalıdır. Birkaç e-kitap dosyasını calibre’ye sürükleyip bırakın veya “Kitap ekle” düğmesini tıklayın ve çalışmak istediğiniz e-kitaplara göz atın. Kitapları ekledikten sonra, ana görünümde şöyle bir şey görünecek şekilde görünecekler:

Kalbinizin içeriğine eklediğiniz kitapların listesine hayran kaldıktan sonra, muhtemelen bir kitap okumak isteyeceksiniz. Bunu yapmak için kitabı okuyucunuzun anlayacağı bir biçime dönüştürmeniz gerekir. Calibre’yi ilk çalıştırırken,: arayüz: Karşılama sihirbazı başlar ve okuyucu cihazınız için calibre kurar. Dönüşüm bir esintidir. Dönüştürmek istediğiniz kitabı seçip “Kitapları dönüştür” düğmesini tıklamanız yeterlidir. Şimdilik tüm seçenekleri yoksay ve “Tamam” ı tıklayın. Sağ alt köşedeki küçük simge dönmeye başlar. Döndürme bittiğinde dönüştürülen kitabınız hazırdır. Kitabı okumak için “Görüntüle” düğmesine tıklayın.

Kitabı okuyucunuzda okumak istiyorsanız, okuyucuyu bilgisayara bağlayın, calibre onu algılayana kadar bekleyin (10-20 saniye) ve ardından “Cihaza gönder” düğmesine tıklayın. Simge tekrar dönmeyi bıraktığında, okuyucunuzun bağlantısını kesin ve okumaya devam edin! Kitabı önceki adımda dönüştürmediyseniz, calibre kitabı okuyucu cihazınızın anlayacağı biçime otomatik olarak dönüştürür.

Daha gelişmiş bir kullanıma başlamak için Grafik Kullanıcı Arayüzü belgesini okumalısınız. Daha da fazla güç ve çeşitlilik için Komut Satırı Arayüzü belgesine bakabilirsiniz. Sıkça Sorulan Sorular belgesini de faydalı bulacaksınız.

Daha fazla sorunuz varsa veya calibre’yi diğer kullanıcılarla tartışmak veya belirli konularda yardım almak istiyorsanız, forumlar ve diğer yardım kaynakları mevcuttur [https://calibre-ebook.com/help].

Bölümler

- Grafik Kullanıcı Arayüzü
- Sık kullandığınız haber sitesini ekleme
- eKitap Görüntüleyici
- E-kitap dönüşümü
- E-kitap düzenleme
- Calibre İçerik sunucusu
- E-kitapları karşılaştır
- E-kitap meta verilerini düzenle
- Sıkça Sorulan Sorular
- Rehberler
- The calibre:// URL scheme
- Calibre’yi özelleştirmek
- Komut Satırı Arayüzü
- Calibre geliştirme ortamı ayarlamak
- Dijital Haklar Yönetimi (DRM)
- Açıklayıcı sözlük

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Grafik Kullanıcı Arayüzü

Grafik Kullanıcı Arayüzü

The Graphical User Interface (GUI) provides access to all library management and e-book format conversion features. The basic workflow for using calibre is to first add books to the library from your hard disk. calibre will automatically try to read metadata from the books and add them to its internal database. Once they are in the database, you can perform various Eylemler on them that include conversion from one format to another, transfer to the reading device, viewing on your computer, and editing metadata. The latter includes modifying the cover, description, and tags among other details. Note that calibre creates copies of the files you add to it. Your original files are left untouched.

Arayüz çeşitli bölümlere ayrılmıştır:

- Eylemler

- Tercihler

- Kataloglar

- Search & sort

- The search interface

- Aramaları kaydetmek

- Searching the full text of all books

- Sanal kitaplıklar

- Temporarily marking books

- Metadata’nın dosya isimlerinden tahmini

- Kitap detayları

- Tag browser

- Kapak ızgarası

- Cover browser

- Adding notes for authors, series, etc.

- Hızlıgörünüm

- Görevler

- Klavye kısayolları

Eylemler

The actions toolbar provides convenient shortcuts to commonly used actions. If you right-click the buttons, you can perform variations on the default action. Please note that the actions toolbar will look slightly different depending on whether you have an e-book reader attached to your computer.

- Kitap ekle

- Nitelik düzenleme

- Kitapları dönüştür

- Görünüm

- Cihaza gönder

- Haberleri çek

- Kitaplık

- Cihaz

- Diske kaydet

- Bağlan/Paylaş

- Kitapları Sil

Kitap ekle

Kitap ekle eylemi düğmeye sağ tıkla erişilen yedi çeşit eylem içerir.

1. Add books from a single folder: Opens a file chooser dialog and allows you to specify which books in a folder should be added. This action is context sensitive, i.e. it depends on which catalog you have selected. If you have selected the Library, books will be added to the library. If you have selected the e-book reader device, the books will be uploaded to the device, and so on.

2. Add books from folders and sub-folders: Allows you to choose a folder. The folder and all its sub-folders are scanned recursively, and any e-books found are added to the library. You can choose whether to have calibre add all files present in a single folder to a single book record or multiple book records. calibre assumes that each folder contains a single book. All e-book files in a folder are assumed to be the same book in different formats. This action is the inverse of the Save to disk action, i.e. you can Save to disk, delete the books and re-add them in single book per folder mode, with no lost information except for the date (this assumes you have not changed any of the setting for the Save to disk action).

3. Add multiple books from archive (ZIP/RAR): Allows you to add multiple e-books that are stored inside the selected ZIP or RAR files. It is a convenient shortcut that avoids having to first unzip the archive and then add the books via one of the above two options.

4. Add empty book (Book Entry with no formats): Allows you to create a blank book record. This can be used to then manually fill out the information about a book that you may not have yet in your collection.

5. ISBN ile ekle: Bir ya da fazla kitabı ISBNlerini ekleyerek girmenizi sağlar.

6. Dosyaları seçili kitap kayıtlarına ekle: Kitaplığınızda var olan bir kitapla ilişkili dosyaları eklemeniz veya güncellemenize izin verir.

7. Add data files to selected book records: Allows you to add any number of extra files that will be stored in a data sub-directory in the book directory. See Bir kitaba fazladan veri dosyaları ekleme for details.

8. Add an empty file to selected book records: Allows you to add an empty file of the specified format to the selected book records.

The Add books action can read metadata from a wide variety of e-book formats. In addition, it tries to guess metadata from the filename. See the Metadata’nın dosya isimlerinden tahmini section, to learn how to configure this.

Mevcut bir kitaba ek bir biçim eklemek için şu üç şeyden birini yapabilirsiniz:

1. Drag and drop the file onto the Book details panel on the right side of the main window

2. Right click the Add books button and choose Add files to selected books.

3. Click the Add books button in the top right area of the Edit metadata dialog, accessed by the Nitelik düzenleme action.

Nitelik düzenleme

Metadata düzenle eyleminin düğmeye sağ tıklayarak erişilen dört çeşit eylemi vardır.

1. Edit metadata individually: Allows you to edit the metadata of books one-by-one with the option of fetching metadata, including covers, from the Internet. It also allows you to add or remove particular e-book formats from a book.

2. Edit metadata in bulk: Allows you to edit common metadata fields for large numbers of books simultaneously. It operates on all the books you have selected in the Library view.

3. Metadata ve kapakları indir: Kitap listesinde seçili ktiaplar için metadata ve kapakları (mevcutsa) indir.

4. Kitap kayıtlarını birleştir: İki veeya daha fazla kitap için metadata ve biçimleri birleştirme olanağı sunar. İlk önce tıklanmamış kayıtları silme veya tutma seçeneğiniz vardır.

5. Manage data files: Manage the extra data files associated with the selected books.

For more details, see E-kitap meta verilerini düzenle.

Kitapları dönüştür

E-books can be converted from a number of formats into whatever format your e-book reader prefers. Many e-books available for purchase will be protected by Digital Rights Management (DRM) technology. calibre will not convert these e-books. It is easy to remove the DRM from many formats, but as this may be illegal, you will have to find tools to liberate your books yourself and then use calibre to convert them.

Çoğu insan için, dönüştürme tek tıklık bir konu olmalıdır. Dönüştürme süreciyle ilgili daha çok şey öğrenmek istiyorsanız, E-kitap dönüşümü göz atın.

Kitapları dönüştür eyleminin düğmeye sağ tıkla erişilen üç çeşidi vardır.

1. Convert individually: Allows you to specify conversion options to customize the conversion of each selected e-book.

2. Bulk convert: Allows you to specify options only once to convert a number of e-books in bulk.

3. Create a catalog of the books in your calibre library: Allows you to generate a complete listing of the books in your library, including all metadata, in several formats such as XML, CSV, BiBTeX, EPUB and MOBI. The catalog will contain all the books currently showing in the library view. This allows you to use the search features to limit the books to be catalogued. In addition, if you select multiple books using the mouse, only those books will be added to the catalog. If you generate the catalog in an e-book format such as EPUB, MOBI or AZW3, the next time you connect your e-book reader the catalog will be automatically sent to the device. For more information on how catalogs work, read the AZW3 • EPUB • MOBI katalogları oluşturma.

Görünüm

The View action displays the book in an e-book viewer program. calibre has a built-in viewer for many e-book formats. For other formats it uses the default operating system application. You can configure which formats should open with the internal viewer via Preferences → Interface → Behavior. If a book has more than one format, you can view a particular format by doing a right-click on the button.

Cihaza gönder

Aygıta gönder eyleminin düğmeye sağ tıkla erişilebilen sekiz değişik çeşidi vardır.

1. Send to main memory: The selected books are transferred to the main memory of the e-book reader.

2. Send to card (A): The selected books are transferred to the storage card (A) on the e-book reader.

3. Send to card (B): The selected books are transferred to the storage card (B) on the e-book reader.

4. Belirli biçimleri gönder: Seçili kitaplar aygıtta belirtilen depolama konumuna, belirttiğiniz biçimde aktarılır.

5. Aygıtı çıkar: Aygıtı calibre’den ayırır.

6. Varsayılan aygıta gönder eylemi ayarla: Ana düğmeye tıkladığınızda hangi seçeneklerin, 1 içinde 5 üstünde veya 7 altında, varsayılan olacağını belirtmenize izin verir.

7. Kitaplıktan sil veya gönder: Seçili kitaplar aygıtta seçilen depolama konumuna aktarılır ve ardından Kitaplıktan silinir.

8. Fetch Annotations (experimental): Transfers annotations you may have made on an e-book on your device to the comments metadata of the book in the calibre library.

You can control the file name and folder structure of files sent to the device by setting up a template in Preferences → Import/export → Sending books to devices. Also see Calibre şablon dili.

Haberleri çek

The Fetch news action downloads news from various websites and converts it into an e-book that can be read on your e-book reader. Normally, the newly created e-book is added to your e-book library, but if an e-book reader is connected at the time the download finishes, the news is also uploaded to the reader automatically.

Haberleri getir eylemi her haber sitesi için (10-15 satır kodluk) basit reçeteler kullanır. Kendi haber kaynaklarınız için nasıl reçete oluşturacağınızı öğrenmek için, bknz Sık kullandığınız haber sitesini ekleme.

Haberleri getir eyleminin sağ tıkla erişilen üç çeşidi vardır.

1. Schedule news download: Allows you to schedule the download of your selected news sources from a list of hundreds available. Scheduling can be set individually for each news source you select and the scheduling is flexible allowing you to select specific days of the week or a frequency of days between downloads.

2. Add a custom news source: Allows you to create a simple recipe for downloading news from a custom news site that you wish to access. Creating the recipe can be as simple as specifying an RSS news feed URL, or you can be more prescriptive by creating Python-based code for the task. For more information, see Sık kullandığınız haber sitesini ekleme.

3. Tüm zamanlanmış haber kaynaklarını indir: Calibre’nin zamanladığınız tüm haber kaynağı indirmelerini hemen başlatmasına neden olur.

Kitaplık

Kitaplık eylemi bir Kitaplık oluşturmanıza, aralarında geçiş yapmanıza, yeniden adlandırmanıza veya silmenize olanak tanır. calibre istediğiniz kadar kitaplık oluşturmanıza izin verir. Örneğin, bir kurgu kitaplığı, bir yabancı dil kitaplığı, proje kitaplığı, veya ihtiyacınıza uyacak herhangi yapıyı oluşturabilirsiniz. Kitaplıklar calibre içindeki en yüksek seviye organizasyonsal yapılardır. Her kitaplığın kendi kitap kümesi, etiketleri, kategorileri ve taban depolama konumu vardır.

1. Kitaplık değiştir/oluştur…: Bu kısım a) başka konumdaki calibre kitaplığına bağlanmanıza, b) yeni bir konumda boş bir kitaplık oluşturmanıza, c) mevcut kitaplığı belirtilmiş yeni bir konuma taşımanıza olanak tanır.

2. Hızlı değiştir: Calibre içinde kayıtlı ya da oluşturulmuş kitaplıklar arasında hızlı geçiş yapmanızı sağlar.

3. Kitaplığı yeniden adlandır: Kitaplığı yeniden adlandırmanıza izin verir.

4. Pick a random book: Chooses a random book in the library for you

5. Remove library: Allows you to unregister a library from calibre.

6. Export/import all calibre data: Allows you to either export calibre data for migration to a new computer or import previously exported data.

7. <library name>: Actions 7, 8 etc… give you immediate switch access between multiple libraries that you have created or attached to. This list contains only the 5 most frequently used libraries. For the complete list, use the Quick Switch menu.

8. Kitaplık bakımı: Mevcut kitaplığı veri tutarlılığı problemleri için kontrol etmenize ve mevcut kitaplığın veri tabanını yedeklerden geri yüklemenize izin verir.

Not

Metadata about your e-books, e.g. title, author, and tags, is stored in a single file in your calibre library folder called metadata.db. If this file gets corrupted (a very rare event), you can lose the metadata. Fortunately, calibre automatically backs up the metadata for every individual book in the book’s folder as an OPF file. By using the Restore database action under Library Maintenance described above, you can have calibre rebuild the metadata.db file from the individual OPF files for you.

Kitapları değişik kitaplıklar arasında (birden fazla kitaplığınız ayarlanmış olduğunda) kitaba sağ tıklayıp Kitaplığa kopyala eylemini seçerek kopyalayabilirsiniz.

Cihaz

Aygıt eylemi aygıtınızın ana belleği ya da depolama kartlarındaki kitapları görüntülemenizi, veya aygıtı çıkarmanızı sağlar (calibre’den ayırmanızı). Desteklenen bir aygıt taktığınızda bu simge calibre ana araç çubuğunda otomatik olarak çıkar. Üzerine tıklayarak cihazınızdaki kitapları görebilirsiniz. Ayrıca calibre kitaplığınızdaki kitapları sürükleyip simge üzerine bırakarak cihazınıza aktarabilirsiniz. Aynı şekilde, aygıtınızdan kitapları sürükleyip araç çubuğundaki kitaplık simgesine bırakarak aygıtınızdan calibre’ye aktarım yapabilirsiniz.

Diske kaydet

Diske kaydet eyleminin sağ tık düğmesiyle erişilebilen beş çeşidi vardır.

1. Save to disk: Saves the selected books to disk organized in folders. The folder structure looks like:

Author_(sort)
Title
Book Files

You can control the file name and folder structure of files saved to disk by setting up a template in Preferences → Import/export → Saving books to disk. Also see Calibre şablon dili.

2. Save to disk in a single folder: Saves the selected books to disk in a single folder.

For 1. and 2., all available formats, as well as metadata, are stored to disk for each selected book. Metadata is stored in an OPF file. Saved books can be re-imported to the library without any loss of information by using the Add books action.

3. Save only *<your preferred>* format to disk: Saves the selected books to disk in the folder structure as shown in (1.) but only in your preferred e-book format. You can set your preferred format in Preferences → Interface → Behaviour → Preferred output format

4. Save only *<your preferred>* format to disk in a single folder: Saves the selected books to disk in a single folder but only in your preferred e-book format. You can set your preferred format in Preferences → Interface → Behaviour → Preferred output format

5. Save single format to disk…: Saves the selected books to disk in the folder structure as shown in (1.) but only in the format you select from the popup list.

Bağlan/Paylaş

The Connect/share action allows you to manually connect to a device or folder on your computer. It also allows you to set up your calibre library for access via a web browser or email.

The Connect/share action has four variations, accessed by doing a right-click on the button.

1. Dizine bağlan: Bilgisayarınızdaki bir dizine bir aygıtmışçasına ve calibre’nin aygıtlar için olan tüm özelliklerini o dizinle kullanacak şekilde bağlanmanıza olanak tanır. Aygıtınız calibre tarafından desteklenemiyor fakat USB aygıtı olarak kullanılabilirse kullanışlıdır.

2. Start Content server: Starts calibre’s built-in web server. When started, your calibre library will be accessible via a web browser from the Internet (if you choose). You can configure how the web server is accessed by setting preferences at Preferences → Sharing → Sharing over the net

3. Kitapların e-posta tabanlı paylaşımını ayarla: Kitap ve haber kaynaklarının e-posta ile paylaşımına izin verir. Bu seçenek için e-posta adresleri ayarlandığında, calibre haber ve kitap güncellemelerini girilen e-posta adresine gönderir. Calibre’nin nasıl e-posta göndereceğini Tercihler → Paylaşım → Kitapların e-posta ile paylaşımı altında ayarlayabilirsiniz. Bir ya da fazla e-posta adresi ayarladığınızda, bu menü girdisi kitapları yapılandırılan e-posta adreslerine gönderen menü girdisiyle değiştirilecektir.

Kitapları Sil

The Remove books action deletes books permanently, so use it with care. It is context sensitive, i.e. it depends on which catalog you have selected. If you have selected the Library, books will be removed from the library. If you have selected the e-book reader device, books will be removed from the device. To remove only a particular format for a given book use the Nitelik düzenleme action. Remove books also has five variations which can be accessed by doing a right-click on the button.

1. Seçili kitapları sil: Kitap listesinde seçilmiş tüm kitapları kalıcı olarak silmenizi sağlar.

2. Remove files of a specific format from selected books…: Allows you to permanently remove e-book files of a specified format from books that are selected in the book list.

3. Remove all formats from selected books, except…: Allows you to permanently remove e-book files of any format except a specified format from books that are selected in the book list.

4. Remove all formats from selected books: Allows you to permanently remove all e-book files from books that are selected in the book list. Only the metadata will remain.

5. Seçili kitaplardan kapakları kaldırır: Kitap listesinde seçili kitaplardan kapak resim dosyalarını kalıcı olarak çıkarır.

6. Remove matching books from device: Allows you to remove e-book files from a connected device that match the books that are selected in the book list.

7. Restore recently deleted: Allows you to undo the removal of books or formats.

Not

Note that when you use Remove books to delete books from your calibre library, the book record is deleted, but the books are temporarily stored, for a few days, in a trash folder. You can undo the delete by right clicking the Remove books button and choosing to Restore recently deleted books.

Tercihler

Tercihler eylemi calibre’nin çeşitli yönlerinin nasıl çalışacağını değiştirme olanağı sunar. Düğmeye sağ tıkla erişilebilen dört çeşidi vardır.

1. Tercihler: Calibre’nin çeşitli yönlerinin çalışma biçimini değiştirmenize olanak sağlar. Düğmeye tıklamak da bu eylemi gerçekleştirir.

2. Run Welcome wizard: Allows you to start the Welcome wizard which appeared the first time you started calibre.

3. Calibre deneyiminizi daha da geliştirmek için eklenti yükleyin: Calibre eklentilerini gösteren yeni bir pencere açar. Bu eklentiler calibre’nin işlevselliğini artırmak için üçüncü taraflar tarafından geliştirilir.

4. Hata ayıklama kipinde yeniden başlat: Calibre geliştiricilerine programda rasladığınız problemlerin çözümünde yardım edebilecek hata ayıklama kipini etkinleştirmeyi sağlar. Çoğu kullanıcı için, bir geliştirici tarafından yönlendirilmediği sürece kapalı kalması uygundur.

Kataloglar

Katalog bir kitap koleksiyonudur. calibre iki çeşit değişik kataloğu yönetebilir:

1. Kitaplık: Bu bilgisayarınızdaki calibre kitaplığında depolanmış kitap koleksiyonudur.

2. Device: This is a collection of books stored in your e-book reader. It will be available when you connect the reader to your computer.

Many operations, such as adding books, deleting, viewing, etc., are context sensitive. So, for example, if you click the View button when you have the Device catalog selected, calibre will open the files on the device to view. If you have the Library catalog selected, files in your calibre library will be opened instead.

Search & sort

Ara & Sırala kısmı kitap koleksiyonunuz üstünde bir çok güçlü eylem gerçekleştirmenizi sağlar.

- Sütun başlıklarına tıklayarak başlık, yazar, tarih, oy, vs. ye göre sıralayabilirsiniz. Ayrıca alt-sıralama yapabillirsiniz, örn. birden çok sütuna göre sıralayabilirsiniz. Başlık sütununa tıklar ardından yazar sütununa tıklarsanız, kitap önce yazara göre sıralanır ardından aynı yazarın tüm girdileri başlığa göre sıralanır.

- You can search for a particular book or set of books using the Search bar. More on that below.

- You can quickly and conveniently edit metadata by selecting the entry you want changed in the list and pressing the E key.

- You can perform Eylemler on sets of books. To select multiple books you can either:

- Ctrl tuşunu basılı tutar ve seçilmesini istediğiniz kitaplara tıklarsınız.

- Shift tuşuna basılı tutarak seçilmesini istediğiniz kitap aralığının başına ve sonuna tıklarsınız.

- Hangi alanların görüntülenmesini istediğinizi yapılandırma iletişim penceresini kullanarak yapılandırabilirsiniz.

- To perform complex multiple column based sub-sorting add the Sort by tool to a toolbar via Preferences → Toolbars & menus.

The search interface

You can search all book metadata by entering search terms in the Search bar. For example:

Asimov Foundation format:lrf

Bu kitaplığınızda LRF biçiminde kullanılabilir olan ve metadata’sında Asimov ve Foundation geçen tüm kitaplarla eşleşir. Biraz daha örnek:

author:Asimov and not series:Foundation
title:"The Ring" or "This book is about a ring"
format:epub publisher:feedbooks.com

Search kinds

There are four search kinds: contains, equality, regular expression (see regular expressions [https://en.wikipedia.org/wiki/Regular_expression]), and character variant. You choose the search kind with a prefix character.

‘Contains’ searches

Searches with no prefix character are contains and are by default case insensitive. An item matches if the search string appears anywhere in the indicated metadata. You can make contains searches case sensitive by checking the option Case sensitive searching in Preferences / Searching. If the search option Unaccented characters match accented characters and punctuation is ignored is checked then a character will match all its variants (e.g., e matches é, è, ê, and ë) and all punctuation and whitespace are ignored. For example, if the Unaccented characters match … option is checked then given the two book titles:

1. Big, Bothéred, and Bad

2. Big Bummer

then these searches find:

- title:"er" matches both (‘e’ matches both ‘é’ and ‘e’).

- title:"g " matches both because spaces are ignored.

- title:"g," matches both because the comma is ignored.

- title:"gb" matches both because ‘, ‘ is ignored in book 1 and spaces are ignored in book 2.

- title:"g b" matches both because comma and space are ignored.

- title:"db" matches #1 because the space in ‘and Bad’ is ignored.

- title:"," matches both (it actually matches all books) because commas are ignored.

If the Unaccented characters match … option is not checked then character variants, punctuation, and spacing are all significant.

You can set only one of the search options Case sensitive searching and Unaccented characters match accented characters and punctuation is ignored.

‘Equality’ searches

Equality searches are indicated by prefixing the search string with an equals sign (=). For example, the query tag:"=science" will match science, but not science fiction or hard science. Character variants are significant: é doesn’t match e.

Two variants of equality searches are used for hierarchical items (e.g., A.B.C): hierarchical prefix searches and hierarchical component searches. The first, indicated by a single period after the equals (=.) matches the initial parts of a hierarchical item. The second, indicated by two periods after the equals (=..) matches an internal name in the hierarchical item. Examples, using the tag History.Military.WWII as the value:

- tags:"=.History" : True. History is a prefix of the tag.

- tags:"=.History.Military" : True. History.Military is a prefix of the tag.

- tags:"=.History.Military.WWII" : True. History.Military.WWII is a prefix of the tag, albeit an improper one.

- tags:"=.Military" : False. Military is not a prefix of the tag.

- tags:"=.WWII" : False. WWII is not a prefix of the tag.

- tags:"=..History" : True. The hierarchy contains the value History.

- tags:"=..Military" : True. The hierarchy contains the value Military.

- tags:"=..WWII" : True. The hierarchy contains the value WWII.

- tags:"=..Military.WWII" : False. The .. search looks for single values.

‘Regular expression’ searches

Regular expression searches are indicated by prefixing the search string with a tilde (~). Any Python-compatible regular expression [https://docs.python.org/library/re.html] can be used. Backslashes used to escape special characters in regular expressions must be doubled because single backslashes will be removed during query parsing. For example, to match a literal parenthesis you must enter \\( or alternatively use super-quotes (see below). Regular expression searches are ‘contains’ searches unless the expression is anchored. Character variants are significant: ~e doesn’t match é.

‘Character variant’ searches

Character variant searches are indicated by prefixing the search string with a caret (^). This search is similar to the contains search (above) except that:

- letter case is always ignored.

- character variants always match each other.

- punctuation and whitespace are always significant.

The search options Unaccented characters match accented characters and punctuation is ignored and Case sensitive searching are ignored. They have no effect on this search’s behavior.

The following compares this search to a contains search assuming the Unaccented characters match… option is checked (see above) given the same two book titles:

1. Big, Bothéred, and Bad

2. Big Bummer

then these character variant searches find:

- title:"^er" matches both (‘e’ matches both ‘é’ and ‘e’)

- title:"^g" matches both

- title:"^g " matches #2 because the space is significant

- title:"^g," matches #1 because the comma is significant

- title:"^gb" matches nothing because space and comma are significant

- title:"^g b" matches #2 because the comma is significant

- title:"^db" matches nothing

- title:"^," matches #1 (instead of all books) because the comma is significant

Search Expression Syntax

A search expression is a sequence of search terms optionally separated by the operators and and or. If two search terms occur without a separating operator, and is assumed. The and operator has priority over the or operator; for example the expression a or b and c is the same as a or (b and c). You can use parenthesis to change the priority; for example (a or b) and c to make the or evaluate before the and. You can use the operator not to negate (invert) the result of evaluating a search expression. Examples:

- not tag:foo finds all books that don’t contain the tag foo

- not (author:Asimov or author:Weber) finds all books not written by either Asimov or Weber.

The above examples show examples of search terms. A basic search term is a sequence of characters not including spaces, quotes ("), backslashes (\), or parentheses (( )). It can be optionally preceded by a column name specifier: the lookup name of a column followed by a colon (:), for example author:Asimov. If a search term must contain a space then the entire term must be enclosed in quotes, as in title:"The Ring". If the search term must contain quotes then they must be escaped with backslashes. For example, to search for a series named The “Ball” and The “Chain”, use:

series:"The \"Ball\" and The \"Chain\"

If you need an actual backslash, something that happens frequently in regular expression searches, use two of them (\\).

It is sometimes hard to get all the escapes right so the result is what you want, especially in regular expression and template searches. In these cases use the super-quote: """sequence of characters""". Super-quoted characters are used unchanged: no escape processing is done.

More information

To search for a string that begins with an equals, tilde, or caret; prefix the string with a backslash.

Enclose search strings with quotes (”) if the string contains parenthesis or spaces. For example, to find books with the tag Science Fiction you must search for tag:"=science fiction". If you search for tag:=science fiction you will find all books with the tag science and the word fiction in any metadata.

You can build advanced search queries easily using the Advanced search dialog accessed by clicking the button on the left of the search box.

Available fields for searching are: tag, title, author, publisher, series, series_index, rating, cover, comments, format, identifiers, date, pubdate, search, size, vl and custom columns. If a device is plugged in, the ondevice field becomes available, when searching the calibre library view. To find the search name (actually called the lookup name) for a custom column, hover your mouse over the column header in the library view.

Dates

Tarih için arama söz dizimi şu şekildedir:

pubdate:>2000-1 Will find all books published after Jan, 2000
date:<=2000-1-3 Will find all books added to calibre before 3 Jan, 2000
pubdate:=2009 Will find all books published in 2009

If the date is ambiguous then the current locale is used for date comparison. For example, in an mm/dd/yyyy locale 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale it is interpreted as 2 Jan 2009. Some special date strings are available. The string today translates to today’s date, whatever it is. The strings yesterday and thismonth (or the translated equivalent in the current language) also work. In addition, the string daysago (also translated) can be used to compare to a date some number of days ago. For example:

date:>10daysago
date:<=45daysago

Çevrilen karakter dizileriyle ilgili olabilecek problemleri önlemek için, calibre’nin İngilizce olmayan bir sürümünü kullanırken, _today, _yesterday, _thismonth, ve _daysago her zaman kullanılabilir tutulmuştur. Bunlar çevrilmez.

Searching dates and numeric values with relational comparisons

Tarih ve sayısal alanlar = (eşittir), > (büyüktür), >= (büyük eşit), < (küçüktür), <= (küçük eşit), ve != (eşit değildir) ilişki operatörlerini destekler. Oylama alanları sayısal olarak ele alınır. Örneğin rating:>=3 araması 3 veya yukarı oy almış tüm kitapları bulur.

You can search for books that have a format of a certain size like this:

- size:>1.1M will find books with a format larger than 1.1MB

- size:<=1K will find books with a format smaller than or equal to 1KB

You can search for the number of items in multiple-valued fields such as tags using the character # then using the same syntax as numeric fields. For example, to find all books with more than 4 tags use tags:#>4. To find all books with exactly 10 tags use tags:#=10.

Series indices

Series indices are searchable. For the standard series, the search name is series_index. For custom series columns, use the column search name followed by _index. For example, to search the indices for a custom series column named #my_series, you would use the search name #my_series_index. Series indices are numbers, so you can use the relational operators described above.

Saved searches

The special field search is used for saved searches. If you save a search with the name “My spouse’s books” you can enter search:"My spouse's books" in the Search bar to reuse the saved search. More about saving searches below.

Virtual libraries

The special field vl is used to search for books in a Virtual library. For example, vl:Read will find all the books in the Read Virtual library. The search vl:Read and vl:"Science Fiction" will find all the books that are in both the Read and Science Fiction virtual libraries. The value following vl: must be the name of a Virtual library. If the Virtual library name contains spaces then surround it with quotes.

Whether a field has a value

You can search for the absence or presence of a value for a field using “true” and “false”. For example:

- cover:false finds all books without a cover

- series:true finds all books that are in a series

- series:false finds all books that are not in a series

- comments:false finds all books with an empty comment

- formats:false finds all books with no book files (empty records)

Yes/no custom columns

Searching Yes/no custom columns for false, empty, or blank will find all books with undefined values in the column. Searching for true will find all books that do not have undefined values in the column. Searching for yes or checked will find all books with Yes in the column. Searching for no or unchecked will find all books with No in the column. Note that the words yes, no, blank, empty, checked and unchecked are translated; you can use either the current language’s equivalent word or the English word. The words true and false and the special values _yes, _no, and _empty are not translated.

Identifiers

Identifiers (e.g., ISBN, DOI, LCCN, etc.) use an extended syntax. An identifier has the form type:value, as in isbn:123456789. The extended syntax permits you to specify independently the type and value to search for. Both the type and the value parts of the query can use any of the search kinds. Examples:

- tanımlayıcılar:true herhangi bir tanımlayıcısı olan kitapları bulur.

- tanımlayıcılar:false herhangi bir tanımlayıcısı olmayan kitapları bulur.

- tanımlayıcılar:123 herhangi türü 123 değerini içeren kitapları arar.

- tanımlayıcılar:=123456789 herhangi türü 123456789 değerine eşit kitapları arar.

- identifiers:=isbn: and identifiers:isbn:true will find books with a type equal to ISBN having any value

- identifiers:=isbn:false will find books with no type equal to ISBN.

- identifiers:=isbn:123 will find books with a type equal to ISBN having a value containing 123.

- identifiers:=isbn:=123456789 will find books with a type equal to ISBN having a value equal to 123456789.

- tanımlayıcılar:i:1 1 değeri içeren i içeren türdeki tüm kitapları arar.

Categories visible in the Tag browser

The search in_tag_browser:true finds all books that are in categories (tags, authors, etc.) currently shown in the Tag browser. This is useful if you set the two preferences Preferences → Look & feel → Tag browser → Hide empty categories and Find shows all items that match. With those two preferences set, doing a Find in the Tag browser shows only categories containing items matched by the Find. Then, the search in_tag_browser:true additionally finds books with these categories / items. You can easily run this search by pressing the key Ctrl+Alt+Shift+F or clicking the configure button in the Tag browser and choosing the Show only books that have visible categories entry.

Search using templates

You can search using a template in Calibre şablon dili instead of a metadata field. To do so you enter a template, a search type, and the value to search for. The syntax is:

template: (the template) #@#: (search type) : (the value)

The template is any valid calibre template language template. The search type must be one of t (text search), d (date search), n (numeric search), or b (set/not set (boolean)). The value is whatever you want, and can use the search kinds described above for the various search types. You must quote the entire search string if there are spaces anywhere in it.

Examples:

- template:"program: connected_device_name('main')#@#:t:kindle" – is true when the kindle device is connected.

- template:"program: select(formats_sizes(), 'EPUB')#@#:n:>1000000" – finds books with EPUB files larger than 1 MB.

- template:"program: select(formats_modtimes('iso'), 'EPUB')#@#:d:>10daysago" – finds books with EPUB files newer than 10 days ago.

- template:"""program: book_count('tags:^"' & $series & '"', 0) != 0#@#:n:1""" – finds all books containing the series name in the tags. This example uses super-quoting because the template uses both single quotes (') and double quotes (") when constructing the search expression.

You can build template search queries easily using the Advanced search dialog accessed by clicking the button . You can test templates on specific books using the calibre Template tester, which can be added to the toolbars or menus via Preferences → Toolbars & menus. It can also be assigned a keyboard shortcut via Preferences → Shortcuts.

Advanced search dialog

Advanced search dialog

Aramaları kaydetmek
calibre allows you to save a frequently used search under a special name and then reuse that search with a single click. To do this, create your search either by typing it in the Search bar or using the Tag browser. Then type the name you would like to give to the search in the Saved Searches box next to the Search bar. Click the plus icon next to the saved searches box to save the search.

Now you can access your saved search in the Tag browser under Saved searches. A single click will allow you to reuse any arbitrarily complex search easily, without needing to re-create it.

Searching the full text of all books
calibre can optionally index the full text of books in the library to allow for instant searching of words inside any book. To use this functionality click the FT button at the left edge of the search bar.

Then, enable indexing for the current library. Once indexing is complete you can search all the text in the full library. When you add new books to the library, they will be automatically indexed in the background. This search allows you to quickly find all books that contain a word or combination of words. You can even search for words that occur near other words, as shown in the examples in the search popup window. Note that this search tool will find only one occurrence of the search query in a particular book, not list every occurrence, for that it is best to search inside the book using the calibre E-book viewer.

You can re-index an individual book by right clicking on the Book details panel in calibre and choosing Re-index this book for full text searching.

Sanal kitaplıklar

A Virtual library is a way to pretend that your calibre library has only a few books instead of its full collection. This is an excellent way to partition your large collection of books into smaller, manageable chunks. To learn how to create and use Virtual libraries, see the tutorial: Sanal kitaplıklar.

Temporarily marking books

You can temporarily mark arbitrary sets of books. Marked books will have a pin on them and can be found with the search marked:true. To mark a book press Ctrl+M or go to Preferences → Toolbars & menus and add the Mark books button to the main toolbar.

You can mark books with a specific text label by right clicking the Mark books button and choosing Mark books with text label. Books marked with text labels can later be found using the search marked:"=the-text-you-entered".

Metadata’nın dosya isimlerinden tahmini

Normally, calibre reads metadata from inside the book file. However, it can be configured to read metadata from the file name instead, via Preferences → Import/export → Adding books → Read metadata from file contents.

You can also control how metadata is read from the filename using regular expressions (see Calibre’de kurallı ifadelerin kullanımına dair her şey). In the Adding books section of the configuration dialog, you can specify a regular expression that calibre will use to try and guess metadata from the names of e-book files that you add to the library. The default regular expression is:

title - author

bu, ilk - işaretine kadar tüm karakterlerin kitap başlığını, takip eden karakterlerin de kitap yazarını oluşturduğunu varsayar. Örneğin, dosya ismi:

Foundation and Earth - Isaac Asimov.txt

başlık: Foundation and Earth ve yazar: Isaac Asimov olarak yorumlanır

Tüyo

Dosya ismi bir tire içermezse, yukarıdaki düzenli ifade başarısız olur.

Kitap detayları

The Book details display shows the cover and all the metadata for the currently selected book. It can be hidden via the Layout button in the lower right corner of the main calibre window. The author names shown in the Book details panel are click-able, they will by default take you to the Wikipedia page for the author. This can be customized by right clicking on the author name and selecting Manage this author.

Similarly, if you download metadata for the book, the Book details panel will automatically show you links pointing to the web pages for the book on Amazon, WorldCat, etc. from where the metadata was downloaded.

You can right click on individual e-book formats in the Book details panel to delete them, compare them to their original versions, save them to disk, open them with an external program, etc.

You can change the cover of the book by simply drag and dropping an image onto the Book details panel. If you wish to edit the cover image in an external program, simply right click on it and choose Open with.

You can also add e-book files to the current book by drag and dropping the files onto the Book details panel.

Double clicking the Book details panel will open it up in a separate popup window.

Finally, you can customize exactly what information is displayed in the Book details panel via Preferences → Interface → Look & feel → Book details.

Tag browser

The Tag browser allows you to easily browse your collection by Author/Tags/Series/etc. If you click on any item in the Tag browser, for example the author name Isaac Asimov, then the list of books to the right is restricted to showing books by that author. You can click on category names as well. For example, clicking on “Series” will show you all books in any series.

The first click on an item will restrict the list of books to those that contain or match the item. Continuing the above example, clicking on Isaac Asimov will show books by that author. Clicking again on the item will change what is shown, depending on whether the item has children (see sub-categories and hierarchical items below). Continuing the Isaac Asimov example, clicking again on Isaac Asimov will restrict the list of books to those not by Isaac Asimov. A third click will remove the restriction, showing all books. If you hold down the Ctrl or Shift keys and click on multiple items, then restrictions based on multiple items are created. For example you could hold Ctrl and click on the tags History and Europe for finding books on European history. The Tag browser works by constructing search expressions that are automatically entered into the Search bar. Looking at what the Tag browser generates is a good way to learn how to construct basic search expressions.

Items in the Tag browser have their icons partially colored. The amount of color depends on the average rating of the books in that category. So for example if the books by Isaac Asimov have an average of four stars, the icon for Isaac Asimov in the Tag browser will be 4/5th colored. You can hover your mouse over the icon to see the average rating.

The outer-level items in the Tag browser, such as Authors and Series, are called categories. You can create your own categories, called User categories, which are useful for organizing items. For example, you can use the User categories editor (click the Configure button at the lower-left of the Tag browser and choose Manage authors, tags, etc → User categories) to create a User category called Favorite Authors, then put the items for your favorites into the category. User categories can have sub-categories. For example, the User category Favorites.Authors is a sub-category of Favorites. You might also have Favorites.Series, in which case there will be two sub-categories under Favorites. Sub-categories can be created by right-clicking on a User category, choosing Add sub-category to…, and entering the sub-category name; or by using the User categories editor by entering names like the Favorites example above.

You can search User categories in the same way as built-in categories, by clicking on them. There are four different searches cycled through by clicking:

1. “kategorideki öğeyle eşleşen herşey” tek yeşil bir artı işareti ile gösterilir.

2. “kategori veya alt kategorideki öğeyle eşleşen herşey” iki yeşil artı işareti ile gösterilir.

3. “kategorideki öğeyle eşleşmeyen herşey” tek kırmızı bir eksi işareti ile gösterilir.

4. “kategori veya alt kategorideki öğeyle eşleşmeyen herşey” iki kırmızı eksi işareti ile gösterilir.

It is also possible to create hierarchies inside some of the text categories such as tags, series, and custom columns. These hierarchies show with the small triangle, permitting the sub-items to be hidden. To use hierarchies of items in a category, you must first go to Preferences → Interface → Look & feel and enter the category name(s) into the “Categories with hierarchical items” field. Once this is done, items in that category that contain periods will be shown using the small triangle. For example, assume you create a custom column called “Genre” and indicate that it contains hierarchical items. Once done, items such as Mystery.Thriller and Mystery.English will display as Mystery with the small triangle next to it. Clicking on the triangle will show Thriller and English as sub-items. See Managing subgroups of books, for example “genre” for more information.

Hierarchical items (items with children) use the same four ‘click-on’ searches as User categories. Items that do not have children use two of the searches: “everything matching” and “everything not matching”.

You can drag and drop items in the Tag browser onto User categories to add them to that category. If the source is a User category, holding the Shift key while dragging will move the item to the new category. You can also drag and drop books from the book list onto items in the Tag browser; dropping a book on an item causes that item to be automatically applied to the dropped books. For example, dragging a book onto Isaac Asimov will set the author of that book to Isaac Asimov. Dropping it onto the tag History will add the tag History to the book’s tags.

You can easily find any item in the Tag browser by clicking the search button at the lower-right corner. In addition, you can right click on any item and choose one of several operations. Some examples are to hide it, rename it, or open a “Manage x” dialog that allows you to manage items of that kind. For example, the Manage authors dialog allows you to rename authors and control how their names are sorted.

You can control how items are sorted in the Tag browser via the Configure button at the lower-left of the Tag browser. You can choose to sort by name, average rating or popularity (popularity is the number of books with an item in your library; for example, the popularity of Isaac Asimov is the number of books in your library by Isaac Asimov).

Kapak ızgarası

You can have calibre display a grid of book covers instead of a list of books, if you prefer to browse your collection by covers instead. The Cover grid is activated by clicking the Layout button in the bottom right corner of the main calibre window. You can customize the cover sizes and the background of the Cover grid via Preferences → Interface → Look & feel → Cover grid. You can even have calibre display any specified field under the covers, such as title or authors or rating or a custom column of your own devising.

Cover browser

In addition to the Cover grid described above, you can also have calibre display covers in the single row. This is activated via the Layout button in the lower right corner of the main window. In Preferences → Interface → Look & feel → Cover browser you can change the number of covers displayed, and even have the Cover browser display itself in a separate popup window.

Adding notes for authors, series, etc.

You can add notes for an author/series/tag/publisher/etc. to your calibre library. To do so right click on the author name in the Tag browser on the left or the Book details panel on the right and choose Create note or Edit note.

A simple popup window will allow you to enter your notes using basic formatting and supporting links and images. Once a note for an author is created, it can be viewed easily from the Book details panel by clicking the little pencil icon next to the author name.

You can search through all the notes in your library using the Browse notes tool by pressing Ctrl+Shift+N or adding it to the toolbar via Preferences → Toolbars & menus.

Hızlıgörünüm

Sometimes you want to select a book and quickly get a list of books with the same value in some category (authors, tags, publisher, series, etc.) as the currently selected book, but without changing the current view of the library. You can do this with Quickview. Quickview opens either a second window or a panel in the book list showing the list of books matching the value of interest. For example, assume you want to see a list of all the books with the one or more of the authors of the currently-selected book. Click in the author cell you are interested in and press the ‘Q’ key or click the Quickview icon in the Layout section of the calibre window. A window or panel will open with all the authors for that book on the left, and all the books by the selected author on the right.

Bazı örnek Hızlıgörünüm kullanımları: diğer kitaplarla ilgili hızlıca bilgi almak için:

- have some tag(s) applied to the currently selected book,

- mevcut kitapla aynı seride olan

- bir özel sütunda mevcut kitapla aynı değerlere sahip olan

- mevcut kitaptaki yazarların biriyle yazılmış olan

- share values in a custom column

There are two choices for where the Quickview information appears:

1. It can open “undocked”: on top of the calibre window and will stay open until you explicitly close it.

2. It can open “docked”: as a panel in the book list section of the calibre main window.

You can move the window from docked to undocked as desired using the “Dock/Undock” button.

The Quickview panel can be left open permanently, in which case it follows movements on the book list. For example, if you click in the calibre library view on a category column (tags, series, publisher, authors, etc.) for a book, the Quickview window contents will change to show you in the left-hand side panel the values in that category for the selected book (e.g., the tags for that book). The first item in that list will be selected, and Quickview will show you on the right-hand side panel all the books in your library that use that value. Click on an different value in the left-hand panel to see the books with that different value.

Double-click on a book in the Quickview window to select that book in the library view. This will also change the items display in the QuickView window (the left-hand panel) to show the items in the newly-selected book.

Shift- or Ctrl- double-click on a book in the Quickview window to open the edit metadata dialog on that book in the calibre window. The edited book will be Quickview’ed when you close the edit metadata dialog.

Bur sütunun Hızlıgörünümde gösterilip gösterilemeyeceğini farenizi sütun başlığı üstüne getirip bu başlık için açılan araç ipucuna bakarak görebilirsiniz. Ayrıca sütun başlığına sağ tıklayarak “Hızlıgörünüm” seçeneğinin menüde gösterilip gösterilmediğine de bakabilirsiniz, bu durumda Hızlıgörünüm seçeneğini seçmek mevcut hücrede ‘Q’ tuşlamakla aynıdır.

Options (in Preferences → Look & feel → Quickview):

- Respect (or not) the current Virtual library. If checked then Quickview shows only books in the current Virtual library. Default: respect Virtual libraries

- Change the Quickview window contents when the column is changed on the book list using the cursor keys. Default: don’t follow changes made with cursor keys

- Change the column being “quickview’ed” when a cell in the Quickview window is double-clicked. Otherwise the book is changed but the column being examined is not. Default: change the column

- Change the column being “quickview’ed” to the current column when the return key is pressed in the Quickview panel. Otherwise the book is changed but the column being examined is not. Default: change the column

- Choose which columns are shown in the Quickview window/panel.

Görevler

The Jobs panel shows the number of currently running jobs. Jobs are tasks that run in a separate process. They include converting e-books and talking to your reader device. You can click on the jobs panel to access the list of jobs. Once a job has completed you can see a detailed log from that job by double-clicking it in the list. This is useful to debug jobs that may not have completed successfully.

Klavye kısayolları
calibre has several keyboard shortcuts to save you time and mouse movement. These shortcuts are active in the book list view (when you’re not editing the details of a particular book), and most of them affect the title you have selected. The calibre E-book viewer has its own shortcuts which can be customised in the viewer Preferences.

Not

Note: The calibre keyboard shortcuts do not require a modifier key (Command, Option, Control, etc.), unless specifically noted. You only need to press the letter key, e.g. E to edit.

Keyboard shortcuts for the main calibre program

Keyboard shortcut

Eylem

F2 (Enter for macOS)

Mevcut seçili alanın metadata’sını kitap listesinde düzenle.

A

Kitap ekle

Shift+A

Add formats to the selected books

C

Convert selected books

D

Cihaza gönder

Del

Seçili kitapları sil

E

Seçili kitapların metadata’sını düzenle

G

Kitap al

I

Show Book details

K

İçindekileri Düzenle

M

Seçili kayıtları birleştir

Alt+M

Seçili kayıtları birleştir, asıllarını koru

O

İçeren dizini aç

P

Kitapları cilala

S

Diske kaydet

T

Kitabı düzenle

V

Görünüm

Shift+V

View last read book

Alt+V/Cmd+V for macOS

Belli bir formatı görüntüle

Alt+Shift+J

Görev listesini tetikle

Alt+Shift+B

Toggle Cover browser

Alt+Shift+D

Toggle Book details panel

Alt+Shift+T

Toggle Tag browser

Alt+Shift+G

Toggle Cover grid

Alt+A

Mevcut kitapla aynı yazara sahip kitapları göster

Alt+T

Mevcut kitapla aynı etikete sahip kitapları göster

Alt+P

Mevcut kitapla aynı yayımcıya sahip kitapları göster

Alt+Shift+S

Mevcut kitapla aynı serideki kitapları göster

/, Ctrl+F

Focus the Search bar

Arama çubuğuna odaklan

Open the Advanced search dialog

Shift+Alt+F

Toggle the Search bar

Esc

Mevcut aramayı temizle

Shift+Esc

Kitap listesine odaklan

Ctrl+Esc

Clear the Virtual library

Alt+Esc

Ek kısıtlamaları temizle

Ctrl+*

Create a temporary Virtual library based on the current search

Ctrl+Right

Select the next Virtual library tab

Ctrl+Left

Select the previous Virtual library tab

N veya F3

Güncel aramayla eşleşen sıradaki kitabı bul (sadece arama tercihlerinde arama vurgulaması açıksa çalışır)

Shift+N veya Shift+F3

Güncel aramayla eşleşen önceki kitabı bul (sadece arama tercihlerinde arama vurgulaması açıksa çalışır)

Ctrl+D

Metaveriyi ve kapakları indir.

Ctrl+R

Calibre’yi yeniden başlatın

Ctrl+Shift+R

Calibre’yi hata ayıklama kipinde yeniden başlat

Shift+Ctrl+E

Calibre’ye boş kitaplar ekle

Ctrl+M

Toggle mark/unmarked status on selected books

Ctrl+/ or Ctrl+Alt+F

Open the popup to search the full text of all books in the library

Q

İlişkili seriler/etiketler/vs. içindeki kitapları görüntülemek için Hızlı Görünüm penceresini aç

Shift+Q

Açılan Hızlı Görünüm paneline odaklan.

Shift+S

Hızlı Görünüm panelinde bir arama yapın

F5

Re-apply the current sort

Ctrl+Q

Calibre’den Çık

X

Toggle auto scroll of the book list

Ctrl+Alt+Shift+F

Restrict the displayed books to only those books that are in a category currently displayed in the Tag browser

B

Browse annotations (highlights and bookmarks) made in the calibre viewer for all books in the library

Ctrl+Shift+N

Browse notes associated with authors/series/tags/etc.

Alt+Shift+L

Toggle the layout between wide and narrow views

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Sık kullandığınız haber sitesini ekleme

Sık kullandığınız haber sitesini ekleme
calibre has a powerful, flexible and easy-to-use framework for downloading news from the Internet and converting it into an e-book. The following will show you, by means of examples, how to get news from various websites.

Uygulama çatısını nasıl kullanacağınızı anlamak için, örnekleri aşağıda listelenen sırada takip edin:

- Tamamı otomatik alınıyor

- Calibre blog

- bbc.co.uk

- Getirme işleminin özelleştirilmesi

- bbc.co.uk nin yazdırma sürümünün kullanılması

- Makale biçemlerinin değiştirilmesi

- Parçalama

- Gerçek dünya örneği

- Yeni reçeteler geliştirmek için ipuçları

- İleri okuma

- API belgelendirmesi

Tamamı otomatik alınıyor

Haber kaynağınız yeterince basitse, calibre tamamen otomatik olarak getirebilir olabilir, tek yapmanız gereken URL’yi sağlamaktır. calibre haber kaynağını bir recipe ye indirmek için gerekli tüm bilgiyi toparlar. calibre’ye bir haber kaynağından bahsetmek için bu haber kaynağı için bir recipe oluşturmalısınız. Biraz örnek görelim:

Calibre blog

The calibre blog is a blog of posts that describe many useful calibre features in a simple and accessible way for new calibre users. In order to download this blog into an e-book, we rely on the RSS feed of the blog:

http://blog.calibre-ebook.com/feeds/posts/default

I got the RSS URL by looking under “Subscribe to” at the bottom of the blog page and choosing Posts → Atom. To make calibre download the feeds and convert them into an e-book, you should right click the Fetch news button and then the Add a custom news source menu item and then the New Recipe button. A dialog similar to that shown below should open up.

First enter Calibre Blog into the Recipe title field. This will be the title of the e-book that will be created from the articles in the above feeds.

Sonraki iki alan (En eski makale ve :guilabel:`Azami makale sayısı `) her beslemeden kaç makale indirileceği konusunu kontrol etmenize izin verir, zaten ne oldukları epey açık.

Beslemeleri reçeteye eklemek için, besleme başlığı ve besleme URL’sini girin ve Besleme ekle düğmesine tıklayın. Besleme eklendikten sonra basitçe Kaydet düğmesine tıklayın, hepsi bu kadar. İletişim penceresini kapatın.

To test your new recipe, click the Fetch news button and in the Custom news sources sub-menu click calibre Blog. After a couple of minutes, the newly downloaded e-book of blog posts will appear in the main library view (if you have your reader connected, it will be put onto the reader instead of into the library). Select it and hit the View button to read!

The reason this worked so well, with so little effort is that the blog provides full-content RSS feeds, i.e., the article content is embedded in the feed itself. For most news sources that provide news in this fashion, with full-content feeds, you don’t need any more effort to convert them to e-books. Now we will look at a news source that does not provide full content feeds. In such feeds, the full article is a webpage and the feed only contains a link to the webpage with a short summary of the article.

bbc.co.uk

Let’s try the following two feeds from The BBC:

1. News Front Page: https://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml

2. Science/Nature: https://newsrss.bbc.co.uk/rss/newsonline_world_edition/science/nature/rss.xml

Follow the procedure outlined in Calibre blog above to create a recipe for The BBC (using the feeds above). Looking at the downloaded e-book, we see that calibre has done a creditable job of extracting only the content you care about from each article’s webpage. However, the extraction process is not perfect. Sometimes it leaves in undesirable content like menus and navigation aids or it removes content that should have been left alone, like article headings. In order, to have perfect content extraction, we will need to customize the fetch process, as described in the next section.

Getirme işleminin özelleştirilmesi

İndirme işlemini mükemmelleştirmeye, ya da karmaşık bir siteden içerik indirmeye çalışırken reçete yazılım çatısının tüm güç ve esnekliğini kullanabilirsiniz. Bunun için Özel haber kaynağı ekle iletişim penceresinde Gelişmiş Kipe geç düğmesine tıklamanız yeterli.

The easiest and often most productive customization is to use the print version of the online articles. The print version typically has much less cruft and translates much more smoothly to an e-book. Let’s try to use the print version of the articles from The BBC.

bbc.co.uk nin yazdırma sürümünün kullanılması

The first step is to look at the e-book we downloaded previously from bbc.co.uk. At the end of each article, in the e-book is a little blurb telling you where the article was downloaded from. Copy and paste that URL into a browser. Now on the article webpage look for a link that points to the “Printable version”. Click it to see the print version of the article. It looks much neater! Now compare the two URLs. For me they were:

Makale URL

https://news.bbc.co.uk/2/hi/science/nature/7312016.stm

Yazdırma sürümü URL’si

https://newsvote.bbc.co.uk/mpapps/pagetools/print/news.bbc.co.uk/2/hi/science/nature/7312016.stm

Öyle görünüyor ki yazdırma sürümünü almak için her makale URL’sine şu ön ek gelmeli:

newsvote.bbc.co.uk/mpapps/pagetools/print/

Now in the Advanced mode of the Custom news sources dialog, you should see something like (remember to select The BBC recipe before switching to advanced mode):

You can see that the fields from the Basic mode have been translated to Python code in a straightforward manner. We need to add instructions to this recipe to use the print version of the articles. All that’s needed is to add the following two lines:

def print_version(self, url):
return url.replace('https://', 'https://newsvote.bbc.co.uk/mpapps/pagetools/print/')

This is Python, so indentation is important. After you’ve added the lines, it should look like:

In the above, def print_version(self, url) defines a method that is called by calibre for every article. url is the URL of the original article. What print_version does is take that url and replace it with the new URL that points to the print version of the article. To learn about Python [https://www.python.org] see the tutorial [https://docs.python.org/tutorial/].

Now, click the Add/update recipe button and your changes will be saved. Re-download the e-book. You should have a much improved e-book. One of the problems with the new version is that the fonts on the print version webpage are too small. This is automatically fixed when converting to an e-book, but even after the fixing process, the font size of the menus and navigation bar become too large relative to the article text. To fix this, we will do some more customization, in the next section.

Makale biçemlerinin değiştirilmesi

Önce bölümde, The BBC nin yazdırılabilir sürümündeki yazı tipi boyutunun çok küçük olduğunu gördük. Bir çok web sitesinde, The BBC dahil, bu yazı tipi CSS stil sayfaları ile ayarlanır. Şu satırı ekleyerek bu gibi stil sayfalarının getirilmesini engelleyebiliriz:

no_stylesheets = True

Tarifi şimdi gibi görünüyor:

Yeni sürüm oldukça iyi görünüyor. Mükemmelliyetçiyseniz, sonraki kısmı da okumak isteyeceksiniz, bu bölüm asıl indirilen içeriğin değiştirilmesiyle ilgilenir.

Parçalama
calibre indirilen içerikte oynama yapmaya gelince bir çok güçlü ve esnek yetenek içerir. Bunlardan bir kaçını göstermek için, eski dostumuz The BBC reçetesine tekrar bakalım. Bir kaç makalenin (yazdırılabilir sürüm) kayna koduna (HTML) baktığımızda işe yarar bir içeriği olmayan alt bilgileri olduğunu görürüz

<div class="footer">
...
</div>

Bunu kaldırmak için şu eklenebilir:

remove_tags = [dict(name='div', attrs={'class':'footer'})]

to the recipe. Finally, lets replace some of the CSS that we disabled earlier, with our own CSS that is suitable for conversion to an e-book:

extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'

With these additions, our recipe has become “production quality”.

Bu reçete calibre’nin gücüyle ilgili buz dağının yalnızca görünen kısmını keşfediyor. Calibre’nin yeteneklerinin daha fazlasını keşfetmek için sonraki kısımda daha karmaşık bir gerçek dünyadan örneğe göz atacağız.

Gerçek dünya örneği

BasicNewsRecipe :term:`API`sini daha çok keşfeden nispeten karmaşık bir gerçek dünya örneği The New York Times :term:`reçete`sidir
import string, re from calibre import strftime from calibre.web.feeds.recipes import BasicNewsRecipe from calibre.ebooks.BeautifulSoup import BeautifulSoup class NYTimes(BasicNewsRecipe):

title = 'The New York Times' __author__ = 'Kovid Goyal' description = 'Daily news from the New York Times' timefmt = ' [%a, %d %b, %Y]' needs_subscription = True remove_tags_before = dict(id='article') remove_tags_after = dict(id='article') remove_tags = [dict(attrs={'class':['articleTools', 'post-tools', 'side_tool', 'nextArticleLink clearfix']}), dict(id=['footer', 'toolsRight', 'articleInline', 'navigation', 'archive', 'side_search', 'blog_sidebar', 'side_tool', 'side_index']), dict(name=['script', 'noscript', 'style'])] encoding = 'cp1252' no_stylesheets = True extra_css = 'h1 {font: sans-serif large;}\n.byline {font:monospace;}'

def get_browser(self): br = BasicNewsRecipe.get_browser(self) if self.username is not None and self.password is not None: br.open('https://www.nytimes.com/auth/login') br.select_form(name='login') br['USERID'] = self.username br['PASSWORD'] = self.password br.submit() return br def parse_index(self): soup = self.index_to_soup('https://www.nytimes.com/pages/todayspaper/index.html')

def feed_title(div): return ''.join(div.findAll(text=True, recursive=False)).strip()

articles = {} key = None ans = [] for div in soup.findAll(True, attrs={'class':['section-headline', 'story', 'story headline']}):

if ''.join(div['class']) == 'section-headline': key = string.capwords(feed_title(div)) articles[key] = [] ans.append(key)

elif ''.join(div['class']) in ['story', 'story headline']: a = div.find('a', href=True) if not a: continue url = re.sub(r'\?.*', '', a['href']) url += '?pagewanted=all' title = self.tag_to_string(a, use_alt=True).strip() description = '' pubdate = strftime('%a, %d %b') summary = div.find(True, attrs={'class':'summary'}) if summary: description = self.tag_to_string(summary, use_alt=False)

feed = key if key is not None else 'Uncategorized' if feed not in articles: articles[feed] = [] if not 'podcasts' in url: articles[feed].append( dict(title=title, url=url, date=pubdate, description=description, content='')) ans = self.sort_index_by(ans, {'The Front Page':-1, 'Dining In, Dining Out':1, 'Obituaries':2}) ans = [(key, articles[key]) for key in ans if key in articles] return ans def preprocess_html(self, soup): refresh = soup.find('meta', {'http-equiv':'refresh'}) if refresh is None: return soup content = refresh.get('content').partition('=')[2] raw = self.browser.open('https://www.nytimes.com'+content).read() return BeautifulSoup(raw.decode('cp1252', 'replace'))

Bu yeni :term:`reçete`de bir çok yeni özellik görüyoruz. Öncelikle:

timefmt = ' [%a, %d %b, %Y]'

This sets the displayed time on the front page of the created e-book to be in the format, Day, Day_Number Month, Year. See timefmt.

Sonra indirilen :term:`HTML`in temizlenmesi için bir grup yönerge görüyoruz:

remove_tags_before = dict(name='h1')
remove_tags_after = dict(id='footer')
remove_tags = ...

Bunlar ilk <h1> etiketinden önceki herşeyi ve footer id’li ilk etiketten sonraki herşeyi kaldırır. Ayrıca bakınız remove_tags, remove_tags_before, remove_tags_after.

Sonraki dikkat çekici özellik:

needs_subscription = True
...
def get_browser(self):
...

needs_subscription = True tells calibre that this recipe needs a username and password in order to access the content. This causes, calibre to ask for a username and password whenever you try to use this recipe. The code in calibre.web.feeds.news.BasicNewsRecipe.get_browser() actually does the login into the NYT website. Once logged in, calibre will use the same, logged in, browser instance to fetch all content. See mechanize [https://mechanize.readthedocs.io/en/latest/] to understand the code in get_browser.

The next new feature is the calibre.web.feeds.news.BasicNewsRecipe.parse_index() method. Its job is to go to https://www.nytimes.com/pages/todayspaper/index.html and fetch the list of articles that appear in todays paper. While more complex than simply using RSS, the recipe creates an e-book that corresponds very closely to the days paper. parse_index makes heavy use of BeautifulSoup [https://www.crummy.com/software/BeautifulSoup/bs4/doc/] to parse the daily paper webpage. You can also use other, more modern parsers if you dislike BeautifulSoup. calibre comes with lxml [https://lxml.de/] and html5lib [https://github.com/html5lib/html5lib-python], which are the recommended parsers. To use them, replace the call to index_to_soup() with the following:

raw = self.index_to_soup(url, raw=True)
# For html5lib
import html5lib
root = html5lib.parse(raw, namespaceHTMLElements=False, treebuilder='lxml')
# For the lxml html 4 parser
from lxml import html
root = html.fromstring(raw)

Son yeni özellik calibre.web.feeds.news.BasicNewsRecipe.preprocess_html() metodudur. İndirilen her HTML sayfasında keyfi dönüştürmeler yapmak için kullanılabilir. Burada nytimes’ın her makaleden önce gösterdiği reklamları geçmek için kullanılıyor.

Yeni reçeteler geliştirmek için ipuçları

The best way to develop new recipes is to use the command line interface. Create the recipe using your favorite Python editor and save it to a file say myrecipe.recipe. The .recipe extension is required. You can download content using this recipe with the command:

ebook-convert myrecipe.recipe .epub --test -vv --debug-pipeline debug

The command ebook-convert will download all the webpages and save them to the EPUB file myrecipe.epub. The -vv option makes ebook-convert spit out a lot of information about what it is doing. The ebook-convert-recipe-input --test option makes it download only a couple of articles from at most two feeds. In addition, ebook-convert will put the downloaded HTML into the debug/input folder, where debug is the folder you specified in the ebook-convert --debug-pipeline option.

Once the download is complete, you can look at the downloaded HTML by opening the file debug/input/index.html in a browser. Once you’re satisfied that the download and preprocessing is happening correctly, you can generate e-books in different formats as shown below:

ebook-convert myrecipe.recipe myrecipe.epub
ebook-convert myrecipe.recipe myrecipe.mobi
...

If you’re satisfied with your recipe, and you feel there is enough demand to justify its inclusion into the set of built-in recipes, post your recipe in the calibre recipes forum [https://www.mobileread.com/forums/forumdisplay.php?f=228] to share it with other calibre users.

Not

MacOS’ta komut satırı araçları calibre paketinin içindedir; örneğin, calibre’yi /Applications dizinine yüklediyseniz komut satırı araçları /Applications/calibre.app/Contents/MacOS/ konumundadır .

Ayrıca bakınız

ebook-convert

The command line interface for all e-book conversion.

İleri okuma

BasicNewsRecipe ile kullanılabilir olan olanakları kullanarak gelişmiş reçeteler yazmakla ilgili daha fazla şey öğrenmek istiyorsanız aşağıdaki kaynaklara başvurabilirsiniz:

API documentation

BasicNewsRecipe sınıfı ve tüm önemli metod ve alanlarının belgeleri.

BasicNewsRecipe [https://github.com/kovidgoyal/calibre/blob/master/src/calibre/web/feeds/news.py]

BasicNewsRecipe kaynak kodu

Yerleşik reçeteler [https://github.com/kovidgoyal/calibre/tree/master/recipes]

Calibre ile gelen yerleşik reçetelerin kaynak kodları

The calibre recipes forum [https://www.mobileread.com/forums/forumdisplay.php?f=228]

Bir çok bilgili calibre reçete geliştiricisi orada takılır.

API belgelendirmesi

API documentation for recipesBasicNewsRecipe

BasicNewsRecipe.abort_article()
BasicNewsRecipe.abort_recipe_processing()
BasicNewsRecipe.add_toc_thumbnail()
BasicNewsRecipe.adeify_images()
BasicNewsRecipe.canonicalize_internal_url()
BasicNewsRecipe.cleanup()
BasicNewsRecipe.clone_browser()
BasicNewsRecipe.default_cover()
BasicNewsRecipe.download()
BasicNewsRecipe.extract_readable_article()
BasicNewsRecipe.get_article_url()
BasicNewsRecipe.get_browser()
BasicNewsRecipe.get_cover_url()
BasicNewsRecipe.get_extra_css()
BasicNewsRecipe.get_feeds()
BasicNewsRecipe.get_masthead_title()
BasicNewsRecipe.get_masthead_url()
BasicNewsRecipe.get_obfuscated_article()
BasicNewsRecipe.get_url_specific_delay()
BasicNewsRecipe.image_url_processor()
BasicNewsRecipe.index_to_soup()
BasicNewsRecipe.is_link_wanted()
BasicNewsRecipe.parse_feeds()
BasicNewsRecipe.parse_index()
BasicNewsRecipe.populate_article_metadata()
BasicNewsRecipe.postprocess_book()
BasicNewsRecipe.postprocess_html()
BasicNewsRecipe.preprocess_html()
BasicNewsRecipe.preprocess_image()
BasicNewsRecipe.preprocess_raw_html()
BasicNewsRecipe.print_version()
BasicNewsRecipe.publication_date()
BasicNewsRecipe.skip_ad_pages()
BasicNewsRecipe.sort_index_by()
BasicNewsRecipe.tag_to_string()
BasicNewsRecipe.articles_are_obfuscated
BasicNewsRecipe.auto_cleanup
BasicNewsRecipe.auto_cleanup_keep
BasicNewsRecipe.center_navbar
BasicNewsRecipe.compress_news_images

BasicNewsRecipe.compress_news_images_auto_size

BasicNewsRecipe.compress_news_images_max_size
BasicNewsRecipe.conversion_options
BasicNewsRecipe.cover_margins
BasicNewsRecipe.delay
BasicNewsRecipe.description
BasicNewsRecipe.encoding
BasicNewsRecipe.extra_css
BasicNewsRecipe.feeds
BasicNewsRecipe.filter_regexps
BasicNewsRecipe.handle_gzip
BasicNewsRecipe.ignore_duplicate_articles
BasicNewsRecipe.keep_only_tags
BasicNewsRecipe.language
BasicNewsRecipe.masthead_url
BasicNewsRecipe.match_regexps
BasicNewsRecipe.max_articles_per_feed
BasicNewsRecipe.needs_subscription
BasicNewsRecipe.no_stylesheets
BasicNewsRecipe.oldest_article
BasicNewsRecipe.preprocess_regexps
BasicNewsRecipe.publication_type
BasicNewsRecipe.recipe_disabled
BasicNewsRecipe.recursions
BasicNewsRecipe.remove_attributes
BasicNewsRecipe.remove_empty_feeds
BasicNewsRecipe.remove_javascript
BasicNewsRecipe.remove_tags
BasicNewsRecipe.remove_tags_after
BasicNewsRecipe.remove_tags_before
BasicNewsRecipe.requires_version
BasicNewsRecipe.resolve_internal_links
BasicNewsRecipe.reverse_article_order
BasicNewsRecipe.scale_news_images
BasicNewsRecipe.scale_news_images_to_device
BasicNewsRecipe.simultaneous_downloads
BasicNewsRecipe.summary_length
BasicNewsRecipe.template_css
BasicNewsRecipe.timefmt
BasicNewsRecipe.timeout
BasicNewsRecipe.title
BasicNewsRecipe.use_embedded_content

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Sık kullandığınız haber sitesini ekleme »
- API documentation for recipes

API documentation for recipes

Reçete yazmaya yarayan API BasicNewsRecipe ile tanımlanmıştır
class calibre.web.feeds.news.BasicNewsRecipe(options, log, progress_reporter)[kaynak]

Tüm reçeteler için gerekli mantığı içeren temel sınıf. Bu sınıf içindeki fonksiyonları kademeli olarak ezerek kademeli olarak daha özelleştirilmiş/güçlü reçeteler oluşturabilirsiniz. Reçete oluşturmakla ilgili bir giriş kılavuzu için şuraya bakınız, Sık kullandığınız haber sitesini ekleme.

abort_article(msg=None)[kaynak]

Mevcut makalenin indirmesini durdurmak için bu yöntemi herhangi bir ön işleme yöntemi içinde çağırın. Tamamen videodan oluşan makaleler gibi uygunsuz içerik içeren makaleleri atlamak için kullanışlıdır.

abort_recipe_processing(msg)[kaynak]

Reçete indirme sisteminin bu reçeteyi indirmeyi durdurmasına sebep olur, kullanıcıya basit bir ger besleme iletisi gösterilir.

add_toc_thumbnail(article, src)[kaynak]

Bunu populate_article_metadata’dan makaledeki <img> etiketinin İçindekiler bölümünde makaleyi temsil etmeye uygun küçük resim için src özniteliği ile çağırın. Küçük resmin gerçekten kullanılıp kullanılmayacağı aygıta bağlıdır (şimdilik sadece Kindle’larda kullanılır). Referans verilen resmin başarılı şekilde indirilmiş olmasına dikkat edin, aksi takdirde yoksayılacaktır.

classmethod adeify_images(soup)[kaynak]

Reçeteniz EPUB’a dönüştürüldüğünde ve Adobe Digital Editions ile görüntülendiğinde resimlerle ilgili sorun yaşıyorsa bu metodu postprocess_html() içinden çağırın.

canonicalize_internal_url(url, is_link=True)[kaynak]

``url``nin kuralsal temsillerinin kümesini döndür. Varsayılan gerçekleme yalnızca sunucu makine adı ve URL yolunu kullanır, sorgu parametrelerini, parçaları, vs göz ardı eder. Kuralsal ifadeler bu haber kaynağının URL’leri arasında benzersiz olmalıdırlar. Olmazlarsa, dahili bağlantılar yanlış şekilde çözümlenebilir.

Parametreler:

is_link – URL bir HTML dosyasındandaki dahili bağlantıdan geliyorsa Doğrudur. URL makaleyi indirmek için kullanılan URL ise Yanlış.

cleanup()[kaynak]

Tüm makaleler indirildikten sonra çağrılır. Aboneli sitelerden çıkış yapmak gibi temizlik işlerini yapmak için kullanın.

clone_browser(br)[kaynak]

Tarayıcıyı coğalt. Çoğaltılmış tarayıcılar çok iş parçacıklı indirmeler yapmak için kullanılır, çünkü mechanize iş parçacığı güvenli değildir. Varsayılan çoğaltma rutinleri çoğu tarayıcı özelleştirmesini yakalıyor olmalıdır, ancak reçetenizde egzotik bir şeyler yaparsanız, reçetenizde bu metodu ezmeli ve elle çoğaltma yapmalısınız.

Çoğaltılmış tarayıcı örnekleri çerez yönetiminde özelleştirme yapmadıysanız varsayılan olarak aynı, iş parçacığı güvenli CookieJar’ı kullanır.

default_cover(cover_file)[kaynak]

Kapağı olmayan reçeteler için genel bir kapak oluştur

download()[kaynak]

Bu reçetedeki beslemelerden makaleleri indir ve ön işleme yap. Bu metod her Reçete örneği için yalnızca bir kere çağrılmalıdır. Birden fazla çağırmak tanımlanmayan davranışlara yol açar. :return: index.html yolu

extract_readable_article(html, url)[kaynak]

‘html’den ana makale içeriğini aktarır, temizler ve demet (article_html, extracted_title) olarak döndürür. Arc90’ın asıl okunabilirlik algortimasına dayanır.

get_article_url(article)[kaynak]

Override in a subclass to customize extraction of the URL that points to the content for each article. Return the article URL. It is called with article, an object representing a parsed article from a feed. See feedparser [https://pythonhosted.org/feedparser/]. By default it looks for the original link (for feeds syndicated via a service like FeedBurner or Pheedo) and if found, returns that or else returns article.link [https://pythonhosted.org/feedparser/reference-entry-link.html].

get_browser(*args, **kwargs)[kaynak]

Return a browser instance used to fetch documents from the web. By default it returns a mechanize [https://mechanize.readthedocs.io/en/latest/] browser instance that supports cookies, ignores robots.txt, handles refreshes and has a mozilla firefox user agent.

Reçeteniz önce giriş yapmanızı gerektiriyorsa, alt sınıfınızda bu metodu ezin. Örneğin, New York Times reçetesinde kullanılan aşağıdaki kod tam erişim için giriş yapar:

def get_browser(self):
br = BasicNewsRecipe.get_browser(self)
if self.username is not None and self.password is not None:
br.open('https://www.nytimes.com/auth/login')
br.select_form(name='login')
br['USERID'] = self.username
br['PASSWORD'] = self.password
br.submit()
return br

get_cover_url()[kaynak]

Bu basımın kapağına URL ya da None döndür. Varsayılan olarak normalde None olan self.cover_url öğesinin değerini döndürür. Reçetenizin e-kitap için bir kapak indirmesini istiyorsanız bu metodu alt sınıfınızda ezin, veya bu metodu çağırmadan önce üye değişken `self.cover_url`yi ayarlayın.

get_extra_css()[kaynak]

By default returns self.extra_css. Override if you want to programmatically generate the extra_css.

get_feeds()[kaynak]

Bu profil için getirilecek RSS beslemeleri listesini döndürür. Listenin her elemanı (başlık, url) biçiminde 2 elemanlı demet şeklinde olmalıdır. Başlık None veya boş karakter dizisi ise, beslemedeki başlık kullanılır. Bu method reçetenizin indirilecek besleme listesini anlamak için bir miktar işlem yapması gereken durumlarda faydalıdır. Durum böyleyse, kendi alt sınıfınızda ezin.

get_masthead_title()[kaynak]

Reçete başlığından başka bir şey kullanmak için alt sınıfta ezin

get_masthead_url()[kaynak]

Bu basım için kolombir resminin URL`sini ya da `None döndür. Varsayılan olarak normalde değeri None olan self.masthead_url üyesinin değerini döndürür. Reçetenizin e-kitap için bir kolombir indirmesini istiyorsanız bu metodu alt sınıfınızda ezin, veya bu metodu çağırmadan üye değişken `self.masthead_url`yi ayarlayın. Kolombir resimleri Kindle MOBI dosyalarında kullanılır.

get_obfuscated_article(url)[kaynak]

If you set articles_are_obfuscated this method is called with every article URL. It should return the path to a file on the filesystem that contains the article HTML. That file is processed by the recursive HTML fetching engine, so it can contain links to pages/images on the web. Alternately, you can return a dictionary of the form: {‘data’: <HTML data>, ‘url’: <the resolved URL of the article>}. This avoids needing to create temporary files. The url key in the dictionary is useful if the effective URL of the article is different from the URL passed into this method, for example, because of redirects. It can be omitted if the URL is unchanged.

Bu metod makale içeriğine otomatik olarak erişmeyi zorlaştırmaya çalışan siteler için faydalı olabilir.

get_url_specific_delay(url)[kaynak]

Return the delay in seconds before downloading this URL. If you want to programmatically determine the delay for the specified URL, override this method in your subclass, returning self.delay by default for URLs you do not want to affect.

Dönüşler:

A floating point number, the delay in seconds.

classmethod image_url_processor(baseurl, url)[kaynak]

Perform some processing on image urls (perhaps removing size restrictions for dynamically generated images, etc.) and return the precessed URL. Return None or an empty string to skip fetching the image.

index_to_soup(url_or_raw, raw=False, as_tree=False, save_raw=None)[kaynak]

Convenience method that takes an URL to the index page and returns a BeautifulSoup [https://www.crummy.com/software/BeautifulSoup/bs4/doc] of it.

url_or_raw: Bir URL ya da karakter dizisi olarak indirilmiş dizin sayfası

is_link_wanted(url, tag)[kaynak]

Bağlantı takip edilmeliyse True, aksi durumda False döndür. Ön tanımlı olarak, indiricinin yoksaymasına sebep olacak NotImplementedError yükseltir.

Parametreler:

- url – Takip edilecek URL

- tag – The tag from which the URL was derived

parse_feeds()[kaynak]

BasicNewsRecipe.get_feeds() tarafından döndürülen beslemelerden makale listeleri oluşturur. Feed nesnesi listesi döndürür.

parse_index()[kaynak]

Bu metod makale listeleri yerine web sayfalarını ayrıştırması gereken reçetelerde uygulanır. Kullanım alanı, yazdırma baskısındaki makaleleri bir “Yazım Baskısı” ile listeleyen haber kaynakları olabilir. Bu fonksiyon uygulanırsa, BasicNewsRecipe.parse_feeds() yerine tercih edilir.

Bir liste döndürmelidir. Listenin her elemanı ('besleme başlığı', makale listesi) biçiminde 2 elemanlı demet olmalıdır.

Her makale listesi şu biçimde sözlük içermelidir:

{
'title' : article title,
'url' : URL of print version,
'date' : The publication date of the article as a string,
'description' : A summary of the article
'content' : The full article (can be an empty string). Obsolete
do not use, instead save the content to a temporary
file and pass a file:///path/to/temp/file.html as
the URL.
}

Örnek olarak, The Atlantic indirmek için olan reçeteye göz atın. Ek olarak makalenin yazarı için ‘author’ ekleyebilirsiniz.

Herhangi sebepten işlemeyi durdurmak ve calibre’nin kullanıcıya hata yerine basit bir ileti göstermesini isterseniz, abort_recipe_processing() metodunu çağırın.

populate_article_metadata(article, soup, first)[kaynak]

Called when each HTML page belonging to article is downloaded. Intended to be used to get article metadata like author/summary/etc. from the parsed HTML (soup).

Parametreler:

- article – A object of class calibre.web.feeds.Article. If you change the summary, remember to also change the text_summary

- soup – Parsed HTML belonging to this article

- first – True iff the parsed HTML is the first page of the article.

postprocess_book(oeb, opts, log)[kaynak]

İndirilmiş e-kitap üstünde gerekli tüm art işlemleri çalıştır.

Parametreler:

- oeb – OEBBook nesnesi

- opts – Dönüştürme seçenekleri

postprocess_html(soup, first_fetch)[kaynak]

Bu metod indirilen her bir HTML dosyası kaynağı ile, bağlantılar ve resimler için ayrıştırıldıktan sonra çağrılır. HTML üzerinde keyfi, güçlü, işleme sonrası işlemler yapmak için kullanılabilir. İşledikten sonra soup döndürmelidir.

Parametreler:

- soup – A BeautifulSoup [https://www.crummy.com/software/BeautifulSoup/bs4/doc/] instance containing the downloaded HTML.

- first_fetch – Bu sayfa makalenin ilk sayfasıysa True.

preprocess_html(soup)[kaynak]

Bu metod indirilen her bir HTML dosyası kaynağı ile, bağlantılar ve resimler için ayrıştırılmadan önce çağrılır. remote_tags vs. de tanımlandığı gibi temizlik yapıldıktan sonra çağrılır. HTML üzerinde keyfi, güçlü işleme öncesi işlemler yapmak için kullanılabilir. İşlemden sonra soup döndürmelidir.

soup: A BeautifulSoup [https://www.crummy.com/software/BeautifulSoup/bs4/doc/] instance containing the downloaded HTML.

preprocess_image(img_data, image_url)[kaynak]

Perform some processing on downloaded image data. This is called on the raw data before any resizing is done. Must return the processed raw data. Return None to skip the image.

preprocess_raw_html(raw_html, url)[kaynak]

Bu metod indirilen her bir HTML dosyası kaynağı ile, bir nesne ağacına ayrıştırılmadan önce çağrılır. raw_html web’den indirilen ham HTML’nin evrensel kodda karakter dizisi temsilidir. url HTML’nin indirildiği URL’dir.

Bu metodun preprocess_regexps’den önce hareket ettiğini unutmayın.

Bu metod işlenmiş raw_html’yi evrensel kod nesnesi olarak döndürmelidir.

classmethod print_version(url)[kaynak]

Makale içeriği olan bir web sayfasını gösteren url`yi al ve makalenin yazdırma sürümünü gösteren bir :term:`URL döndür. Varsayılan olarak hiçbir şey yapmaz. Örneğin:

def print_version(self, url):
return url + '?&pagewanted=print'

publication_date()[kaynak]

Use this method to set the date when this issue was published. Defaults to the moment of download. Must return a datetime.datetime object.

skip_ad_pages(soup)[kaynak]

Bu metod indirilen her HTML dosyasının kaynağı ile, remove_tags, keep_only_tags gibi herhangi bir temizlik özniteliği uygulanmadan önce çağrılır. preprocess_regexps’in zaten uygulanmış olacağını unutmayın. Reçetenin reklam sayfalarını atlamasına olanak tanımak için düşünülmüştür. Eğer soup bir reklam sayfasını temsil ederse, gerçek sayfanın HTML’sini döndür. Aksi halde None döndür.

soup: A BeautifulSoup [https://www.crummy.com/software/BeautifulSoup/bs4/doc/] instance containing the downloaded HTML.

sort_index_by(index, weights)[kaynak]

index`deki başlıkları `weights`e göre sıralamaya yarayan kolaylık sağlayıcı metod. `index yerinde sıralanır. index döndürür.

index: Başlık listesi.

weights: Ağırlıkları başlığa haritalayan bir sözlük. Index’deki herhangi bir başlık ağırlıklı olmazsa, 0 ağırlıklı olduğu varsayılır.

classmethod tag_to_string(tag, use_alt=True, normalize_whitespace=True)[kaynak]

Convenience method to take a BeautifulSoup [https://www.crummy.com/software/BeautifulSoup/bs4/doc/] Tag and extract the text from it recursively, including any CDATA sections and alt tag attributes. Return a possibly empty Unicode string.

use_alt: True ise metinsel içeriği olmayan etiketler için alt öznitelik kullanmaya çalış
tag: BeautifulSoup [https://www.crummy.com/software/BeautifulSoup/bs4/doc/] Tag

articles_are_obfuscated = False

İçerik almasını zorlaştıran web sitelerini işlemek için True olarak ayarlayın ve get_obfuscated_article() metodunu uygulayın.

auto_cleanup = False

İndirilmiş makale sayfalarından tüm metni otomatik olarak çıkart. Okunabilirlik projesinden algoritmalar kullanır. True olarak ayarlamak, indirilmiş HTML’yi temizlemekle elle uğraşmanıza gerek olmadığı anlamına gelir (Elle temizlik daha üstündür tabi).

auto_cleanup_keep = None

Otomatik temizleme algoritmasının asla kaldırmaması gereken öğeleri belirtin. Söz dizim XPath ifadesidir. Örneğin:

auto_cleanup_keep = '//div[@id="article-image"]' will keep all divs with
id="article-image"
auto_cleanup_keep = '//*[@class="important"]' will keep all elements
with class="important"
auto_cleanup_keep = '//div[@id="article-image"]|//span[@class="important"]'
will keep all divs with id="article-image" and spans
with class="important"

center_navbar = True

True ise gezinti çubuğu ortada hizalanır, yoksa sola hizalanır

compress_news_images = False

Set this to False to ignore all scaling and compression parameters and pass images through unmodified. If True and the other compression parameters are left at their default values, JPEG images will be scaled to fit in the screen dimensions set by the output profile and compressed to size at most (w * h)/16 where w x h are the scaled image dimensions.

compress_news_images_auto_size = 16

The factor used when auto compressing JPEG images. If set to None, auto compression is disabled. Otherwise, the images will be reduced in size to (w * h)/compress_news_images_auto_size bytes if possible by reducing the quality level, where w x h are the image dimensions in pixels. The minimum JPEG quality will be 5/100 so it is possible this constraint will not be met. This parameter can be overridden by the parameter compress_news_images_max_size which provides a fixed maximum size for images. Note that if you enable scale_news_images_to_device then the image will first be scaled and then its quality lowered until its size is less than (w * h)/factor where w and h are now the scaled image dimensions. In other words, this compression happens after scaling.

compress_news_images_max_size = None

Set JPEG quality so images do not exceed the size given (in KBytes). If set, this parameter overrides auto compression via compress_news_images_auto_size. The minimum JPEG quality will be 5/100 so it is possible this constraint will not be met.

conversion_options = {}

İndirilen içeriğin e-kitaba dönüştürülmesini kontrol eden reçeteye özel seçenekler. Bunlar kullanıcı veya eklentiye özel ayarları ezecektir, bu yüzden yalnızca gerekliyse kullanın. Örneğin:

conversion_options = {
'base_font_size' : 16,
'linearize_tables' : True,
}

cover_margins = (0, 0, '#ffffff')

By default, the cover image returned by get_cover_url() will be used as the cover for the periodical. Overriding this in your recipe instructs calibre to render the downloaded cover into a frame whose width and height are expressed as a percentage of the downloaded cover. cover_margins = (10, 15, ‘#ffffff’) pads the cover with a white margin 10px on the left and right, 15px on the top and bottom. Color names are defined here [https://www.imagemagick.org/script/color.php]. Note that for some reason, white does not always work in Windows. Use #ffffff instead

delay = 0

The default delay between consecutive downloads in seconds. The argument may be a floating point number to indicate a more precise time. See get_url_specific_delay() to implement per URL delays.

description = ''

Bu reçetenin indirdiği içeriği tanımlayan bir kaç satır. Bu daha çok reçete listesini sunan GUI’de kullanılacaktır.

encoding = None

Hatalı karakter seti tanımlamasına sahip sitelerde kullanılmak üzere bir karakter kodlaması belirleyin. En sık kullanılan latin1 ve cp1252 dir. None ise, kodlama tahmin edilmeye çalışılır. Eğer çağrılabilirse, iki bağımsız değişken ile çağrılır: Reçete nesnesi ve kodu çözülecek kaynak. Kodu çözülmüş kaynağı döndürmelidir.

extra_css = None

Specify any extra CSS that should be added to downloaded HTML files. It will be inserted into <style> tags, just before the closing </head> tag thereby overriding all CSS except that which is declared using the style attribute on individual HTML tags. Note that if you want to programmatically generate the extra_css override the get_extra_css() method instead. For example:

extra_css = '.heading { font: serif x-large }'

feeds = None

İndirilecek besleme listesi. [url1, url2, ...] veya [('title1', url1), ('title2', url2),...] olabilir

filter_regexps = []

Hangi bağlantıların göz ardı edileceğine karar veren düzenli ifade listesi. Boşsa göz ardı edilir. Yalnızca is_link_wanted uygulanmamışsa kullanılır. Örneğin:

filter_regexps = [r'ads\.doubleclick\.net']

içinde ads.doubleclick.net olan tüm URL’leri kaldırır.

BasicNewsRecipe.match_regexps veya BasicNewsRecipe.filter_regexps den yalnızca biri tanımlanmalıdır.

handle_gzip = True

Set to False if you do not want to use gzipped transfers. Note that some old servers flake out with gzip

ignore_duplicate_articles = None

Bir kısımdan fazla yerde olan makalelerin kopyalarını göz ardı et. Kopya makale, aynı başlık ve/veya URL’ye sahip makaledir. Aynı başlıktaki makaleleri göz ardı etmek için, şu şekilde ayarlayın:

ignore_duplicate_articles = {'title'}

URL’ler kullanmak için, şöyle ayarlayın:

ignore_duplicate_articles = {'url'}

Başlık veya URL eşleştirme için, şu şekilde ayarlayın:

ignore_duplicate_articles = {'title', 'url'}

keep_only_tags = []

Yalnızca belirtilen etiketleri ve onların alt etiketlerini sakla. Bir etiketi belirtme formatı için bakınız BasicNewsRecipe.remove_tags. Bu liste boş değilse, <body> etiketi boşaltılır ve bu listedeki girdilerle eşleşen etiketlerle tekrar doldurulur. Örneğin:

keep_only_tags = [dict(id=['content', 'heading'])]

yalnızca “content” veya “heading”`in `id özniteliği olan etiketleri tutar.

language = 'und'

Haberlerin dili. İki veya üç karakter uzunluğunda ISO-639 kodu olmalıdır

masthead_url = None

By default, calibre will use a default image for the masthead (Kindle only). Override this in your recipe to provide a URL to use as a masthead.

match_regexps = []

Hangi bağlantıların takip edileceğini belirten düzenli ifade listesi. Boşsa, göz ardı edilir. Yalnızca is_link_wanted uygulanmamışsa kullanılır. Örneğin:

match_regexps = [r'page=[0-9]+']

içinde page=some number olan tüm URL’lerle eşleşir.

BasicNewsRecipe.match_regexps veya BasicNewsRecipe.filter_regexps den yalnızca biri tanımlanmalıdır.

max_articles_per_feed = 100

Her beslemeden indirilecek azami makale sayısı. Makale tarihleri olmayan beslemeler için özellikle faydalıdır. Çoğu besleme için BasicNewsRecipe.oldest_article kullanmalısınız

needs_subscription = False

True is GUI indirme sırasında kullanıcı ve parola sorar. “isteğe bağlı” olarak ayarlanmışsa kullanıcı adı ve parola isteğe bağlıdır

no_stylesheets = False

Convenient flag to disable loading of stylesheets for websites that have overly complex stylesheets unsuitable for conversion to e-book formats. If True stylesheets are not downloaded and processed

oldest_article = 7.0

Bu haber kaynağından indirilecek en eski makale. Gün olarak.

preprocess_regexps = []

İndirilen HTML üzerince çalıştırılacak regexp yer değiştirme kuralları listesi. Listenin her elemanı iki elemanlı bir demet olmalıdır. İlk eleman derlenmiş bir düzenli ifade, ikincisi tek bir eşleşme nesnesi alan ve eşleşmeyle yer değişecek karakter dizisi döndüren bir çağrılabilir olmalıdır. Örneğin:

preprocess_regexps = [
(re.compile(r'<!--Article ends here-->.*</body>', re.DOTALL|re.IGNORECASE),
lambda match: '</body>'),
]

<!–Article ends here–> ve </body> arasındaki herşeyi kaldırır.

publication_type = 'unknown'

Yayın biçimi gazete, magazin veya blok olarak ayarlanır. None olarak ayarlanırsa, opf dosyasına yayın biçimi metadata’sı yazılmaz.

recipe_disabled = None

Bu reçeteyi kapatmak için boş olmayan bir karakter dizisi olarak ayarlayın. Karakter dizisi kapatma iletisi olarak kullanılacaktır

recursions = 0

Makale web sitelerinde takip edilecek bağlantı seviyesi sayısı

remove_attributes = []

Tüm etiketlerden kaldırılacak öznitelik listesi. Örneğin:

remove_attributes = ['style', 'font']

remove_empty_feeds = False

True ise boş beslemeler çıktıdan kaldırılır. parse_index alt sınıfta ezilmişse bu seçeneğin hiçbir etkisi yoktur. Yalnızca feeds veya get_feeds() kullanarak bir besleme listesi döndüren reçeteler içindir. Ayrıca ignore_duplicate_articles seçeneğini kullanırsanız da kullanılır.

remove_javascript = True

Convenient flag to strip all JavaScript tags from the downloaded HTML

remove_tags = []

Kaldırılacak etiket listesi. Belirtilen etiketler indirilen HTML’den çıkarılır. Bir etiket şu biçimdeki sözlük olarak tanımlanır:

{
name : 'tag name', #e.g. 'div'
attrs : a dictionary, #e.g. {'class': 'advertisment'}
}

All keys are optional. For a full explanation of the search criteria, see Beautiful Soup [https://www.crummy.com/software/BeautifulSoup/bs4/doc/#searching-the-tree] A common example:

remove_tags = [dict(name='div', class_='advert')]

Bu tüm <div class=”advert”> etiketlerini ve alt etiketlerini indirilen :term:`HTML`den çıkarır.

remove_tags_after = None

Belirtilen etiketten sonra gelen tüm etiketleri kaldır. Bir etiketi belirtme biçimi için bakınız BasicNewsRecipe.remove_tags. Örneğin:

remove_tags_after = [dict(id='content')]

id=”content” e sahip ilk elemandan sonraki tüm etiketleri kaldırır.

remove_tags_before = None

Belirtilen etiketten önce gelen tüm etiketleri kaldır. Bir etiketi belirtme biçimi için bakınız BasicNewsRecipe.remove_tags. Örneğin:

remove_tags_before = dict(id='content')

id=”content” e sahip ilk elemandan önceki tüm etiketleri kaldırır.

requires_version = (0, 6, 0)

Bu reçeteyi kullanabilmek için gerekli asgari calibre sürümü

resolve_internal_links = False

Doğru olarak ayarlanmışsa indirilen makalelerdeki diğer indirilen makaleleri gösteren bağlantılar makalenin asıl web URL’si yerine indirilen kopyasını gösterecek şekilde değiştirilir. Bu seçeneği Doğru olarak ayarlarsanız, ayrıca web sitenizin URL şablonuyla çalışması için canonicalize_internal_url() gerçekleştirmeniz de gerekebilir.

reverse_article_order = False

Her beslemedeki makalelerin sırasını tersine çevir

scale_news_images = None

Resimlerin ölçeklendirileceği azamı boyut (w,h). scale_news_images_to_device True ise çıktı profilinde ayarlanan aygıt ekran boyutuna ayarlanır, profilin ayarlanmadığı durumlarda hangi değer atanmışsa o bırakılır (varsayılan None).

scale_news_images_to_device = True

Resmi çıktı profilinde ayarlanmış aygıt ekran boyutlarına sığması için ölçeklendir. Çıktı profili ayarlanmamışsa göz ardı edilir.

simultaneous_downloads = 5

Eş zamanlı indirme sayısı. Sunucu titizse 1 olarak ayarlanır. BasicNewsRecipe.delay > 0 ise otomatik olarak 1’e düşürülür

summary_length = 500

Kısa tanımlamadaki azami karakter sayısı

template_css = '\n            .article_date {\n                color: gray; font-family: monospace;\n            }\n\n            .article_description {\n                text-indent: 0pt;\n            }\n\n            a.article {\n                font-weight: bold; text-align:left;\n            }\n\n            a.feed {\n                font-weight: bold;\n            }\n\n            .calibre_navbar {\n                font-family:monospace;\n            }\n    '

Şablonları biçimlendirmek için kullanılan CSS, ör., gezinti çubukları ve İçindekiler. Bu değişkeni ezmek yerine, doku ve görünümü özelleştirmek için reçetenizde extra_css kullanmalısınız.

timefmt = ' [%a, %d %b %Y]'

İlk sayfada gösterilen tarih için biçim karakter dizisi. Varsayılanı: Gün_Adı, Gün_Numarası Ay_Adı Yıl

timeout = 120.0

Sunucudan dosyaları indirirken kullanılacak saniye cinsinden zaman aşımı

title = 'Bilinmeyen Haber Kaynağı'

The title to use for the e-book

use_embedded_content = None

Normalde bir beslemede tüm makalenin gömülü olup olmadığını gömülü içeriğin uzunluğuna bakarak tahmin etmeye çalışırız. None ise, varsayılan tahmin kullanılır. True ise beslemelerin gömülü içeriği olduğunu, False ise beslemenin gömülü içeriği olmadığını var sayarız.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- eKitap Görüntüleyici

eKitap Görüntüleyici

Calibre tüm önemli eKitap formatlarını görüntüleyebilen dahili bir eKitap Görüntüleyici içerir. eKitap görüntüleyici oldukça özelleştirilebilir ve birçok gelişmiş özelliğe sahiptir.

- eKitap Görüntüleyici’yi Başlatmak

- Bir eKitapta Gezinmek

- Highlighting text

- Read aloud

- Searching the text

- Following links using only the keyboard

- Okuma deneyiminizin görünümünü özelleştirmek

- Sözlük araması

- Metin ve Görüntü Kopyalamak

- Zooming in on images

- Klavye kısayolları

- Non re-flowable content

- Designing your book to work well with the calibre viewer

eKitap Görüntüleyici’yi Başlatmak

You can view any of the books in your calibre library by selecting the book and pressing the View button. This will open up the book in the E-book viewer. You can also launch the E-book viewer by itself from the Start menu in Windows. On macOS, you can pin it to the dock and launch it from there. On Linux you can use its launcher in the desktop menus or run the command ebook-viewer.

Bir eKitapta Gezinmek

You can “turn pages” in a book by either:

- Clicking in the left or right margin or the page with the mouse

- Pressing the spacebar, page up, page down or arrow keys

- On a touchscreen tapping on the text or swiping left and right

You can access the viewer controls by either:

- Right clicking on the text

- Pressing the Esc or Menu keys

- On a touchscreen by tapping the top 1/3rd of the screen

The viewer has two modes, “paged” and “flow”. In paged mode the book content is presented as pages, similar to a paper book. In flow mode the text is presented continuously, like in a web browser. You can switch between them using the viewer Preferences under Page layout or by pressing the Ctrl+M key.

Yer İmleri

When you are in the middle of a book and close the E-book viewer, it will remember where you stopped reading and return there the next time you open the book. You can also set bookmarks in the book by using the Bookmarks button in the E-book viewer controls or pressing Ctrl+B. When viewing EPUB format books, these bookmarks are actually saved in the EPUB file itself. You can add bookmarks, then send the file to a friend. When they open the file, they will be able to see your bookmarks. You can turn off this behavior in the Miscellaneous section of the viewer preferences.

İçindekiler Tablosu

If the book you are reading defines a Table of Contents, you can access it by pressing the Table of Contents button. This will bring up a list of sections in the book. You can click on any of them to jump to that portion of the book.

Konuma Göre Gezinme

E-books, unlike paper books, have no concept of pages. You can refer to precise locations in e-books using the Go to → Location functionality in the viewer controls.

You can use this location information to unambiguously refer to parts of the books when discussing it with friends or referring to it in other works. You can enter these locations under Go to → Location in the viewer controls.

There is a URL you can copy to the clipboard and paste into other programs or documents. Clicking on this URL will open the book in the calibre E-book viewer at the current location.

If you click on links inside the e-book to take you to different parts of the book, such as an endnote, you can use the Back and Forward buttons in the top left corner of the viewer controls. These buttons behave just like those in a web browser.

Kaynak kipi
calibre also has a very handy Reference mode. You can turn it on by clicking the Reference mode button in the viewer controls. Once you do this, every paragraph will have a unique number displayed at the start, made up of the section and paragraph numbers.

You can use this number to unambiguously refer to parts of the books when discussing it with friends or referring to it in other works. You can enter these numbers in the Go to function to navigate to a particular reference location.

Highlighting text

When you select text in the viewer, a little popup bar appears next to the selection. You can click the highlight button in that bar to create a highlight. You can add notes and change the color of the highlight. On a touch screen, long tap a word to select it and show the popup bar. Once in highlight mode you can change what text is selected, using touch screen friendly selection handles. Drag the handles to the top or bottom margins to scroll while selecting. You can also Shift+click or right click to extend the selection, particularly useful for multi-page selections.

You can use the Highlights button in the viewer controls to show a separate panel with a list of all highlights in the book, sorted by chapter.

You can browse all highlights in your entire calibre library by right clicking the View button and choosing Browse annotations.

Finally, if you use the calibre Content server’s in browser viewer, you can have the viewer sync its annotations with the browser viewer by going to Preferences → Miscellaneous in the viewer preferences and entering the username of the Content server viewer to sync with. Use the special value * to sync with anonymous users.

Read aloud

The viewer can read book text aloud. To use it you can simply click the Read aloud button in the viewer controls to start reading book text aloud. The word being currently read is highlighted. Speech is synthesized from the text using your operating system services for text-to-speech. You can change the voice being used by clicking the gear icon in the bar that is displayed while Read aloud is active.

You can also read aloud highlighted passages by adding the Read aloud button to the selection bar in the viewer preferences under Selection behavior.

Not

Support for text-to-speech in browsers is very incomplete and bug-ridden so how well Read aloud will work in the in-browser viewer is dependent on how well the underlying browser supports text-to-speech. In particular, highlighting of current word does not work, and changing speed or voice will cause reading to start again from the beginning.

Not

On Linux, Read aloud requires Speech Dispatcher [https://freebsoft.org/speechd] to be installed and working.

Not

On Windows, not all installed voices may be visible to the SAPI sub-system that is used for text-to-speech. There are instructions to make all voices visible [https://www.mobileread.com/forums/showpost.php?p=4084051&postcount=108].

Searching the text

The viewer has very powerful search capabilities. Press the Ctrl+F key or access the viewer controls and click search. The simplest form of searching is to just search for whatever text you enter in the text box. The different forms of searching are chosen by the search mode box below the search input. Available modes are:

1. Contains - The simplest default mode. The text entered in the search box is searched for anywhere. All punctuation, accents and spaces are ignored. For example, the search: Pena will match all of the following: penal, pen a, pen.a and Peña. If you select the Case sensitive box then accents, spaces and punctuation are no longer ignored.

2. Whole words - Searches for whole words. So for example, the search pena will match the word Peña but not the word Penal. As with Contains searches above, accents and punctuation are ignored unless the Case sensitive box is checked.

3. Nearby words - Searches for whole words that are near each other. So for example, the search calibre cool will match places where the words calibre and cool occur within sixty characters of each other. To change the number of characters add the new number to the end of the list of words. For instance, calibre cool awesome 120 will match places where the three words occur within 120 characters of each other. Note that punctuation and accents are not ignored for these searches.

4. Regex - Interprets the search text as a regular expression. To learn more about using regular expressions, see the tutorial.

Following links using only the keyboard

The E-book viewer has a Hints mode that allows you to click links in the text without using the mouse. Press the Alt+F key and all links in the current screen will be highlighted with a number or letter over them. Press the letter on your keyboard to click the link. Pressing the Esc key will abort the Hints mode without selecting any link.

If more than thirty five links are on-screen then some of them will have multiple letters, in which case type the first and second, or the first and press Enter to activate. You can also use the Backspace key to undo a mistake in typing.

Okuma deneyiminizin görünümünü özelleştirmek

You can change font sizes on the fly by using Font size in the viewer controls or Ctrl++ or Ctrl+- or holding the Ctrl key and using the mouse wheel.

Colors can be changed in the Colors section of the viewer preferences.

You can change the number of pages displayed on the screen as well as page margins in Page layout in the viewer preferences.

You can display custom headers and footers such as time left to read, current chapter title, book position, etc. via the Headers and footers section of the viewer preferences.

More advanced customization can be achieved by the Styles settings. Here you can specify a background image to display under the text and also a stylesheet you can set that will be applied to every book. Using it you can do things like change paragraph styles, text justification, etc. For examples of custom stylesheets used by calibre’s users, see the forums [https://www.mobileread.com/forums/showthread.php?t=51500].

Sözlük araması

You can look up the meaning of words in the current book by double clicking or long tapping the word you want to lookup and then clicking the lookup button that looks like a library.

Metin ve Görüntü Kopyalamak

You can select text and images by dragging the content with your mouse and then right clicking and selecting Copy to copy to the clipboard. The copied material can be pasted into another application as plain text and images.

Zooming in on images

You can zoom in to show an image at full size in a separate window by either double clicking or long tapping on it. You can also right click on it and choose View image.

Klavye kısayolları

The viewer has extensive keyboard shortcuts, like the rest of calibre. They can be customised in the viewer Preferences. The default shortcuts are listed below:

Keyboard shortcuts for the calibre viewer

Keyboard shortcut

Eylem

Home, Ctrl+ArrowUp, Ctrl+ArrowLeft

Scroll to the start of the current file in a multi file book

Ctrl+Home

Scroll to the beginning of the book

Ctrl+End

Scroll to the end of the book

End, Ctrl+ArrowDown, Ctrl+ArrowRight

Scroll to the end of the current file in a multi file book

ArrowUp

Scroll backwards, smoothly in flow mode and by screen fulls in paged mode

ArrowDown

Scroll forwards, smoothly in flow mode and by screen fulls in paged mode

ArrowLeft

Scroll leftwards by a little in flow mode and by a page in paged mode

ArrowRight

Scroll rightwards by a little in flow mode and by a page in paged mode

PageUp, Shift+Spacebar

Scroll backwards by screen-fulls

PageDown, Spacebar

Scroll forwards by screen-fulls

Ctrl+PageUp

Önceki bölüme kaydır

Ctrl+PageDown

Sonraki bölüme kaydır

Alt+ArrowLeft

Geri

Alt+ArrowRight

İleri

Ctrl+T

Toggle Table of Contents

Ctrl+S

Read aloud

Alt+P

Change settings quickly by creating and switching to profiles

Alt+f

Follow links with the keyboard

Ctrl+C

Panoya kopyala

Alt+C

Copy current location to clipboard

Ctrl+Shift+C

Copy current location as calibre:// URL to clipboard

/, Ctrl+f, Cmd+f

Aramayı başlat

F3, Enter

Sonrakini bul

Shift+F3, Shift+Enter

Öncekini bul

Ctrl+Plus, Meta+Plus

Yazı tipi boyutunu büyüt

Ctrl+Minus, Meta+Minus

Yazı tipi boyutunu küçült

Ctrl+0

Restore default font size

Ctrl+]

Increase number of pages per screen

Ctrl+[

Decrease number of pages per screen

Ctrl+Alt+C

Make number of pages per screen automatic

F11, Ctrl+Shift+F

Tam ekranı değiştir

Ctrl+M

Toggle between Paged mode and Flow mode for text layout

Ctrl+W

Toggle the scrollbar

Ctrl+X

Referans modunu değiştir

Ctrl+B

Show/hide bookmarks

Ctrl+Alt+B

New bookmark

Ctrl+N, Ctrl+E

Show the book metadata

Ctrl+Alt+F5, Ctrl+Alt+R

Ktabı tekrar yükle

Ctrl+Shift+ArrowRight

Alter the current selection forward by a word

Ctrl+Shift+ArrowLeft

Alter the current selection backwards by a word

Shift+ArrowRight

Alter the current selection forward by a character

Shift+ArrowLeft

Alter the current selection backwards by a character

Shift+ArrowDown

Alter the current selection forward by a line

Shift+Home

Extend the current selection to the start of the line

Shift+End

Extend the current selection to the end of the line

Ctrl+A

Tümünü seç

Shift+ArrowUp

Alter the current selection backwards by a line

Ctrl+Shift+ArrowDown

Alter the current selection forward by a paragraph

Ctrl+Shift+ArrowUp

Alter the current selection backwards by a paragraph

Esc, MenuKey

E-kitap görüntüleyici kontrollerini göster

Ctrl+Comma, Ctrl+Esc, Meta+Esc, Meta+Comma

E-kitap görüntüleyici tercihlerini göster

Ctrl+G, ;, :

Go to a specified book location or position

Ctrl+Spacebar

Toggle auto-scroll

Alt+ArrowUp

Auto scroll faster

Alt+ArrowDown

Auto scroll slower

Ctrl+I

Show/hide Inspector

Ctrl+L

Show/hide the word lookup panel

Ctrl+Q (Cmd+Q on macOS)

Çık

Ctrl+P

Kitabı PDF’ye yazdır

Ctrl+F11

Toggle the toolbar

Ctrl+H

Toggle the highlights panel

Ctrl+D

Bu kitabı düzenle

Non re-flowable content

Some books have very wide content that cannot be broken up at page boundaries. For example tables or <pre> tags. In such cases, you should switch the viewer to flow mode by pressing Ctrl+M to read this content. Alternately, you can also add the following CSS to the Styles section of the viewer preferences to force the viewer to break up lines of text in <pre> tags:

code, pre { white-space: pre-wrap }

Designing your book to work well with the calibre viewer

The calibre viewer will set the is-calibre-viewer class on the root element. So you can write CSS rules that apply only for it. Additionally, the viewer will set the following classes on the body element:

body.calibre-viewer-dark-colors

Set when using a dark color scheme

body.calibre-viewer-light-colors

Set when using a light color scheme

body.calibre-viewer-paginated

Set when in paged mode

body.calibre-viewer-scrolling

Set when in flow (non-paginated) mode

body.calibre-footnote-container

Set when displaying a popup footnote

Finally, you can use the calibre color scheme colors via CSS variables [https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties]. The calibre viewer defines the following variables: --calibre-viewer-background-color, --calibre-viewer-foreground-color and optionally --calibre-viewer-link-color in color themes that define a link color.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- E-kitap dönüşümü

E-kitap dönüşümü
calibre’nin kullanımı çok kolay olacak şekilde tasarlanmış bir dönüştürme sistemi vardır. Normalde calibre’ye bir kitap eklersiniz, dönüştür’e tıklarsınız ve calibre girdiye mümkün olduğu kadar yakın çıktı üretmek için çok çabalar. Ancak calibre çok sayıda giriş formatını kabul eder ve bunların hepsi e-kitaplara dönüştürme için diğerleri kadar uygun değildir. Bu tür giriş formatları durumunda veya dönüştürme sistemi üzerinde daha fazla kontrol istiyorsanız, calibre’nin dönüştürme sürecine ince ayar yapmak için birçok seçeneği vardır. Ancak calibre’nin dönüştürme sisteminin tam gelişmiş bir e-kitap düzenleyicinin yerini alamayacağını unutmayın. E-kitapları düzenlemek için, önce onları calibre kullanarak EPUB veya AZW3’e dönüştürmenizi ve ardından onları mükemmel bir şekle sokmak için Kitabı düzenle özelliğini kullanmanızı öneririm. Daha sonra düzenlenen e-kitabı calibre’deki diğer formatlara dönüştürmek için girdi olarak kullanabilirsiniz.

Bu belge çoğunlukla aşağıda resmi olan dönüştürme penceresinde bulunan dönüştürme ayarlarına değinecektir. Tüm bu ayarlar ayrıca ebook-convert belgesinde belgelenen dönüştürme komut satırı arayüzü ile de kullanılabilirdir. Calibre’de, tüm ayarlar için yardım metnini fareyi ayar üzerinde tutarak edinebilirsiniz, ilgili ayarı anlatan bir araç ipucu çıkacaktır.

İçindekiler

- Giriş

- Görünüm

- Sayfa yapısı

- Heuristic processing

- Search & replace

- Structure detection

- İçindekiler

- HTML girdi belgelerini dönüştürürken resimler bölüm başlığı olarak kullanılıyor

- İçindekiler bölümündeki girdilere metin temin etmek için etiket öznitelikleri kullanılıyor

- How options are set/saved for conversion

- Biçime özel ipuçları

Giriş

Dönüşüm sistemi hakkında anlaşılması gereken ilk şey, bunun düz bir hat şeklinde tasarlandığıdır. Sistemin şematik görünüşü şöyledir:

The input format is first converted to XHTML by the appropriate Input plugin. This HTML is then transformed. In the last step, the processed XHTML is converted to the specified output format by the appropriate Output plugin. The results of the conversion can vary greatly, based on the input format. Some formats convert much better than others. A list of the best source formats for conversion is available here.

XHTML çıktısına etki eden dönüşümler, tüm işin gerçekleştiği yerdir. Örneğin kitap meta verilerini kitabın başına bir sayfa olarak eklemek, bölüm başlıklarını tespit etmek ve otomatik olarak bir İçindekiler Tablosu oluşturmak, yazı tipi boyutlarını orantılı olarak ayarlamak vb. gibi çeşitli dönüşümler vardır. Tüm dönüşümlerin, giriş dosyasının kendisi üzerinde değil, Giriş eklentisi tarafından XHTML çıkışı üzerinde etkili olduğunu unutmamak önemlidir. Yani, örneğin, calibre’den bir RTF dosyasını EPUB’a dönüştürmesini isterseniz, bu dosya önce dahili olarak XHTML’ye dönüştürülecek, çeşitli dönüşümler XHTML’ye uygulanacak ve ardından Çıktı eklentisi EPUB dosyasını oluşturarak otomatik olarak oluşturacaktır. tüm meta veriler, İçindekiler Tablosu vb.

hata ayıklama seçeneğini kullanarak bu işlemi çalışırken görebilirsiniz. Hata ayıklama çıktısı için klasörün yolunu belirtmeniz yeterlidir. Dönüştürme sırasında calibre, dönüştürme hattının çeşitli aşamaları tarafından oluşturulan XHTML’yi farklı alt klasörlere yerleştirir. Dört alt klasör şunlardır:

Dönüşüm hattı aşamaları

Dizin

Açıklama

girdi

Bu, Giriş eklentisinin HTML çıktısını içerir. Giriş eklentisindeki hataları ayıklamak için bunu kullanın.

çözümlenmiş

Giriş eklentisinden gelen çıktının ön işlenmesi ve XHTML’ye dönüştürülmesinin sonucu. Yapı tespitinde hata ayıklamak için kullanın.

yapı

CSS düzleme ve yazı tipi boyutu dönüştürmeden önce, yapı sonrası algılama. Yazı tipi ve CSS dönüşümlerindeki hataları ayıklamak için kullanın.

işlenmiş

E-kitap Çıktı eklentisine aktarılmadan hemen önce. Çıkış eklentisinde hata ayıklamak için kullanın.

Calibre’nin dönüştürmesini yapmadan önce giriş belgesini biraz düzenlemek isterseniz, yapılacak en iyi şey input alt klasöründeki dosyaları düzenlemek, sonra onu sıkıştırıp ZIP dosyasını giriş olarak kullanmaktır. sonraki dönüşümler için format. Bunu yapmak için Meta bilgileri düzenle iletişim kutusunu kullanarak ZIP dosyasını kitabın formatı olarak ekleyin ve ardından dönüştürme iletişim kutusunun sol üst köşesinde giriş formatı olarak ZIP’i seçin.

Bu belge esas olarak ara XHTML üzerinde çalışan çeşitli dönüşümleri ve bunların nasıl kontrol edileceğini ele alacaktır. Sonunda her giriş/çıkış formatına özel bazı ipuçları bulunmaktadır.

Görünüm

İçindekiler

- Fontlar

- Metin

- Yerleşim

- Stil

- Stilleri dönüştür

- Transform HTML

Bu seçenek grubu, dönüştürülen e-kitabın görünüm ve hissinin çeşitli yönlerini kontrol eder.

Fontlar

E-kitap okuma deneyiminin en güzel özelliklerinden biri kitabın yazı tipi boyutunu kişisel tercih ve aydınlatma koşllarına uygun olarak kolayca ayarlayabilmektir. Calibre, girdi olarak verilen dökümandaki yazı tipi boyutları ne olursa olsun, çıktı olarak ürettiği e-kitaplarda tutarlı yazı tipi boyutları oluşturmak için karmaşık algoritmalara sahiptir.

Bir dökümanın baz yazı tipi boyutu, o dökümandaki en sık kullanılan yazı tipi boyutu ya da diğer bir ifadeyle dökümandaki içeriğin büyük kısmında kullanılan boyuttur. Bir Baz yazı tipi boyutu belirlediğinizde, Calibre otomatik olarak dökümandaki bütün yazı tipi boyutlarını bu değere oransal olarak yeniden ayarlar, böylece en çok kullanılan yazı tipi boyutu belirlenen boyut olur ve diğer yazı tipi büyüklükleri de buna uygun olarak belirlenir. Örneğin daha büyük bir baz font boyutu belirleyerek, dökümandaki tüm yazı tipi boyutlarının büyümesi (Ya da daha küçük baz yazı tipi seçildiğinde tüm yazı tipi boyutlarının küçülmesi) sağlanabilir. En iyi sonuca ulaşabilmek için bir baz yazı tipi büyüklüğü belirlediğinizde yazı tipi boyutu anahtar değerinin de belirlenmesi yerinde olacaktır.

Normally, calibre will automatically choose a base font size appropriate to the output profile you have chosen (see Sayfa yapısı). However, you can override this here in case the default is not suitable for you.

Yazı tipi boyutu anahtarı opsiyonu baz yazı tipi dışındaki yazı tiplerinin nasıl boyutlandırılacağının kontrol edilmesini sağlar. Bu opsiyon temelde yazı tipi boyutlarının birbirinden virgülle ayrıldığı bir listedir. Anahtar değeri Calibre’ye bir yazı tipinin baz yazı tipine oranla kaç “Adım” büyük ya da küçük olacağını belirtir. Normalde bir dökümanda sınırlı sayıda yazı tipi boyutu kullanılmış olmalıdır. Örneğin “normal metinler için bir boyut, farklı derecedeki başlıklar için birkaç boyut ve dipnot vb. kullanımlar için daha birkaç boyut” gibi. Yazı tipi boyutu anahtarı Calibre’ye, girdi dökümandaki farklı yazı tipi boyutlarını farklı “kategoriler” altında gruplandırma olanağı sağlar.

Bir örnek üzerinden gidecek olursak; Kaynak dökümanımız 8pt. baz yazı tipi boyutuna sahip olsun. Bu durumda dökümandaki metnin büyük kısmı 8pt olacaktır, başlıklar biraz daha büyük (Mesela 10 ve 12 pt) ve dipnotlar da 6pt gibi daha küçük bir boyutta olsun. Eğer bu örnek için aşağıdaki ayarları kullanırsak:

Base font size : 12pt
Font size key : 7, 8, 10, 12, 14, 16, 18, 20

Çıktı 12pt baz yazı tipi boyutuna sahipken başlıklar 14 ve 16 pt, dipnotlar ise 8pt olacaktır. Şimdi varsayalım ki büyük başlıkların ve dipnotların boyutunu biraz daha büyüterek bunları öne çıkarmak istiyoruz. Bunun için yazı tipi anahtarının değeri şu şekilde güncellenir:

New font size key : 7, 9, 12, 14, 18, 20, 22

Şimdi büyük başlıklar 18pt olurken dipnotlar 9pt olarak boyutlandırılacak. Hangisinin sizin için en uygun olduğunu anlamak için bu değerleri yazı tipi boyutlandırma sihirbazını kullanarak değiştirebilirsiniz. Bu sihirbaza Yazı tipi boyutu anahtarı bölümünün hemen yanındaki küçük buton ile ulaşabilirsiniz.

Girdi belgesindeki yazı tipi boyutlarını korumak isterseniz, tüm yazı tipi yeniden ölçekleme işlemi buradan kapatılabilir.

A related setting is Line height. Line height controls the vertical height of lines. By default, (a line height of 0), no manipulation of line heights is performed. If you specify a non-default value, line heights will be set in all locations that don’t specify their own line heights. However, this is something of a blunt weapon and should be used sparingly. If you want to adjust the line heights for some section of the input, it’s better to use the Extra CSS.

In this section you can also tell calibre to embed any referenced fonts into the book. This will allow the fonts to work on reader devices even if they are not available on the device.

Metin

Text can be either justified or not. Justified text has extra spaces between words to give a smooth right margin. Some people prefer justified text, others do not. Normally, calibre will preserve the justification in the original document. If you want to override it you can use the Text justification option in this section.

You can also tell calibre to Smarten punctuation which will replace plain quotes, dashes and ellipses with their typographically correct alternatives. Note that this algorithm is not perfect so it is worth reviewing the results. The reverse, namely, Unsmarted punctuation is also available.

Finally, there is Input character encoding. Older documents sometimes don’t specify their character encoding. When converted, this can result in non-English characters or special characters like smart quotes being corrupted. calibre tries to auto-detect the character encoding of the source document, but it does not always succeed. You can force it to assume a particular character encoding by using this setting. cp1252 is a common encoding for documents produced using Windows software. You should also read İngiliz alfabesi dışında karakterler, veya akıllı tırnak içeren dosyamı nasıl dönüştürürüm? for more on encoding issues.

Yerleşim

Normalde XHTML’de paragraflar aralarında bir satır boşluk olacak şekilde ve takip eden metin girintisi bulunmadan gerçeklenir. Calibre’nin bunu kontrol etmek için bir takım seçenekleri vardır. Paragraflar arası boşlukları kaldır tüm paragrafların iç paragraf boşluğu olmamasını garantiler. Ayrıca paragrafın başlangıcını işaretlemek için metin girintisini 1.5em olarak ayarlar (değiştirilebilir). :guilabel: Boş satır ekle tam tersini yapar, her paragraf arası bir boş satır olduğundan emin olur. Bu iki seçenek de çok kapsamlıdır, boşlukların kaldırılması, veya tüm paragraflara eklenmesi (teknik olarak <p> ve <div> etiketleri). Bunun sebebi seçeneği ayarladıktan sonra yapacağını söylediği şeyi yaptığından emin olmanızı sağlamaktır, girdi dosyası ne kadar dağınık olsa da. Bir istisna girdi dosyasının iç paragraflar için boşluksuz satır başı yaptığı durumdur.

If you want to remove the spacing between all paragraphs, except a select few, don’t use these options. Instead add the following CSS code to Extra CSS:

p, div { margin: 0pt; border: 0pt; text-indent: 1.5em }
.spacious { margin-bottom: 1em; text-indent: 0pt; }

Then, in your source document, mark the paragraphs that need spacing with class=”spacious”. If your input document is not in HTML, use the Debug option, described in the Introduction to get HTML (use the input sub-folder).

Another useful options is Linearize tables. Some badly designed documents use tables to control the layout of text on the page. When converted these documents often have text that runs off the page and other artifacts. This option will extract the content from the tables and present it in a linear fashion. Note that this option linearizes all tables, so only use it if you are sure the input document does not use tables for legitimate purposes, like presenting tabular information.

Stil

The Extra CSS option allows you to specify arbitrary CSS that will be applied to all HTML files in the input. This CSS is applied with very high priority and so should override most CSS present in the input document itself. You can use this setting to fine tune the presentation/layout of your document. For example, if you want all paragraphs of class endnote to be right aligned, just add:

.endnote { text-align: right }

ya da tüm paragraflardaki girintilemeyi değiştirmek için:

p { text-indent: 5mm; }

Fazladan CSS çok güçlü bir seçenektir, ama tam anlamıyla kullanabilmek için CSS nasıl çalışır iyice anlamış olmalısınız. Girdi belgenizde hangi CSS’lerin mevcut olduğunu öğrenmek için aşağıda belirtilen hata ayıklama boru hattı seçeneğini kullanabilirsiniz.

A simpler option is to use Filter style information. This allows you to remove all CSS properties of the specified types from the document. For example, you can use it to remove all colors or fonts.

Stilleri dönüştür

This is the most powerful styling related facility. You can use it to define rules that change styles based on various conditions. For example you can use it to change all green colors to blue, or remove all bold styling from the text or color all headings a certain color, etc.

Transform HTML

Similar to transform styles, but allows you to make changes to the HTML content of the book. You can replace one tag with another, add classes or other attributes to tags based on their content, etc.

Sayfa yapısı

The Page setup options are for controlling screen layout, like margins and screen sizes. There are options to setup page margins, which will be used by the output plugin, if the selected output format supports page margins. In addition, you should choose an Input profile and an output profile. Both sets of profiles basically deal with how to interpret measurements in the input/output documents, screen sizes and default font rescaling keys.

If you know that the file you are converting was intended to be used on a particular device/software platform, choose the corresponding input profile, otherwise just choose the default input profile. If you know the files you are producing are meant for a particular device type, choose the corresponding output profile. Otherwise, choose one of the Generic output profiles. If you are converting to MOBI or AZW3 then you will almost always want to choose one of the Kindle output profiles. Otherwise, your best bet for modern E-book reading devices is to choose the Generic e-ink HD output profile.

The output profile also controls the screen size. This will cause, for example, images to be auto-resized to be fit to the screen in some output formats. So choose a profile of a device that has a screen size similar to your device.

Heuristic processing

Heuristic processing provides a variety of functions which can be used to try and detect and correct common problems in poorly formatted input documents. Use these functions if your input document suffers from poor formatting. Because these functions rely on common patterns, be aware that in some cases an option may lead to worse results, so use with care. As an example, several of these options will remove all non-breaking-space entities, or may include false positive matches relating to the function.

Sezgisel işlemeyi etkinleştir

This option activates calibre’s Heuristic processing stage of the conversion pipeline. This must be enabled in order for various sub-functions to be applied

Satırları kaydırma

Bu seçeneği etkinleştirmek calibre’nin bir belgede bulunan boşluksuz satır başlarını noktalama işaretleri ipuçları ve satır uzunluğu kullanarak algılayıp düzeltmeye çalışmasını sağlar. calibre önce boşluksuz satır başlarının mevcut olup olmadığını algılamaya çalışacak, yok gibi görünüyorsa da calibre satır kaydırmak yapmayacaktır. Satır kaydırma yapmama etmeni calibre’yi satır kaydırma yapmamaya ‘zorlamak’ istiyorsanız düşürülebilir.

Satır-kaydırmama etmeni

This option controls the algorithm calibre uses to remove hard line breaks. For example, if the value of this option is 0.4, that means calibre will remove hard line breaks from the end of lines whose lengths are less than the length of 40% of all lines in the document. If your document only has a few line breaks which need correction, then this value should be reduced to somewhere between 0.1 and 0.2.

Biçimsiz bölüm başlıkları ve alt başlıkları algıla ve işaretle

If your document does not have chapter headings and titles formatted differently from the rest of the text, calibre can use this option to attempt to detect them and surround them with heading tags. <h2> tags are used for chapter headings; <h3> tags are used for any titles that are detected.

This function will not create a TOC, but in many cases it will cause calibre’s default chapter detection settings to correctly detect chapters and build a TOC. Adjust the XPath under Structure detection if a TOC is not automatically created. If there are no other headings used in the document then setting “//h:h2” under Structure detection would be the easiest way to create a TOC for the document.

Eklenmiş başlıklar biçimlendirilmiş değiller, biçimleme eklemek için Görünüm ve Doku dönüştürme ayarları altındaki Ek CSS seçeneğini kullanın. Örneğin, başlık etiketlerini ortalamak için, şunu kullanın:

h2, h3 { text-align: center }

<h1> veya <h2> etiket serisini yeniden numaralandır

Bazı dağıtımcılar bölüm başlıklarını birden çok <h1> veya <h2> etiketlerini sırayla kullanarak biçimlendirirler. calibre’nin varsayılan dönüştürme ayarları bu gibi konu başlıklarının iki parçaya ayrılmasına sebep olur. Bu seçenek ayrılmayı önlemek için başlık etiketlerini yeniden numaralandırır.

Paragraflar arası boş satırları sil

This option will cause calibre to analyze blank lines included within the document. If every paragraph is interleaved with a blank line, then calibre will remove all those blank paragraphs. Sequences of multiple blank lines will be considered scene breaks and retained as a single paragraph. This option differs from the Remove paragraph spacing option under Look and Feel in that it actually modifies the HTML content, while the other option modifies the document styles. This option can also remove paragraphs which were inserted using calibre’s Insert blank line option.

Sahne aralarının tutarlı biçimlendirildiğinden emin ol

Bu seçenekle, calibre, sık rastlanan sahne arası işaretçilerini algılamaya ve merkezde hizalandıklarından emin olmaya çalışır. ‘Yumuşak’ sahne arası işaretçilerinin, ör. sadece ek bir boşlukla belirtilen sahne aralarının, sayfa aralarıyla karışmayacak şekilde biçimlendiğinden emin olunur.

Sahne aralarını değiştir

Bu seçenek yapılandırılmışsa, calibre kullanıcı tarafından belirtilen metinle bulduğu sahne arası işaretçilerini değiştirir. Lütfen bazı süsleyici karakterlerin tüm okuma aygıtlarında desteklenmiyor olabileceğini not edin.

In general you should avoid using HTML tags, calibre will discard any tags and use pre-defined markup. <hr /> tags, i.e. horizontal rules, and <img> tags are exceptions. Horizontal rules can optionally be specified with styles, if you choose to add your own style be sure to include the ‘width’ setting, otherwise the style information will be discarded. Image tags can used, but calibre does not provide the ability to add the image during conversion, this must be done after the fact using the ‘Edit book’ feature.

Example image tag (place the image within an ‘Images’ folder inside the EPUB after conversion):

<img style=”width:10%” src=”../Images/scenebreak.png” />

Biçemi olan örnek yatay kural:

<hr style=”width:20%;padding-top: 1px;border-top: 2px ridge black;border-bottom: 2px groove black;”/>

Gereksiz kısa çizgileri sil

bu seçenek etkinken calibre belgedeki tüm tireli içeriği çözümler. Çözümleme için belgenin kendisi sözlük olarak kullanılır. Bu, calibre’nin herhangi bir dilde herhangi bir kelimedeki tireleri doğru şekilde kaldırmasına izin verir, uydurma ve belirsiz bilimsel kelimeler dahil. Birincil eksikliği belgede yalnızca bir kere geçen kelimelerin değiştirilmeyecek olmasıdır. Çözümleme iki aşamada olur, ilk aşama satır sonlarını inceler. Satırlar yalnızce kelime belgede tireli veya tiresiz mevcutsa kaydırılır. İkinci aşama belgedeki tüm tireli kelimeleri inceler, kelime belgede başka bir yerde eşleşme olmadan mevcutsa tireler kaldırılır.

Yaygın kelime ve kalıpları yatık yap

Etkinleştirildiğinde, calibre yaygın kelime ve kalıpları arayıp yatık yazı ifade edenleri yatık hale getirir. Örnek verecek olursak, ~kelime~ gibi yaygın metinler, veya genel olarak yatık yazılması gereken deyimler, örn. latin kalıplardan ‘etc.’ veya ‘et cetera’.

Birim girintilerini CSS girintileriyle değiştir

Some documents use a convention of defining text indents using non-breaking space entities. When this option is enabled calibre will attempt to detect this sort of formatting and convert them to a 3% text indent using CSS.

Search & replace

Bu seçenekler birincil olarak PDF belgeleri ve OCR dönüşümleri için yararlıdır, tabi bir çok belgeye özel problemi çözmek için de kullanılabilirler. Örnek olarak, bazı dönüşümler metinde sayfa başlıkları ya da altbilgileri bırakabilirler. Bu seçenekler düzenli ifadeler kullanarak başlık, altbilgi veya keyfi metinleri bulmaya çalışır ve bu metinleri kaldırır veya değiştirir. Dönüştürme hattındaki ara XHTML çıktı üzerinde işlem yaptıklarını unutmayın. Belgeniz için düzenli ifadeleri özelleştirmenize yarayan bir sihirbaz da mevcuttur. İfade kutusu yanındaki sihirli değneğe tıklayın ve arama ifadenizi yazdıktan sonra ‘Test’ düğmesine tıklayın. Başarılı eşleşmeler Sarı olarak vurgulanacaktır.

The search works by using a Python regular expression. All matched text is simply removed from the document or replaced using the replacement pattern. The replacement pattern is optional, if left blank then text matching the search pattern will be deleted from the document. You can learn more about regular expressions and their syntax at Calibre’de kurallı ifadelerin kullanımına dair her şey.

Structure detection

Yapı algılama, yapılar tanımlanmadığında calibre’nin girdi belgesinde yapısal elemanları algılamak için elinden geleni yapması işini içerir. Örneğin bölümler, sayfa sonları, başlıklar, altbilgi, vs. Tahmin edeceğiniz gibi, bu işlem kitaptan kitaba farklılık gösterir. Neyse ki calibre bunu kontrol etmek için çok güçlü seçeneklere sahiptir. Bu güçle beraber karmaşıklık gelir, ama bu karmaşıklığı öğrenmek için biraz zaman ayırırsanız, çabanıza deydiğini göreceksiniz.

Bölümler ve sayfa sonları
calibre’nin bölüm algılama ve sayfa sonu ekleme için iki set seçeneği vardır. Bazen biraz kafa karıştırıcı olsa da, calibre varsayılan olarak, algılanan bölümlerden önce olduğu gibi sayfa sonları seçeneği ile algılanan yerlerden önce de sayfa sonu ekler. Bunun sebebi yaygın olarak bölüm sınırlarında olmadığı halde sayfa sonu eklenmesi gereken yerlerin bulunmasıdır. Ayrıca, algılanan bölümler isteğe bağlı olarak otomatik üretilen İçindekilere eklenebilir.

calibre uses XPath, a powerful language to allow the user to specify chapter boundaries/page breaks. XPath can seem a little daunting to use at first, fortunately, there is a XPath tutorial in the User Manual. Remember that Structure detection operates on the intermediate XHTML produced by the conversion pipeline. Use the debug option described in the Giriş to figure out the appropriate settings for your book. There is also a button for a XPath wizard to help with the generation of simple XPath expressions.

By default, calibre uses the following expression for detecting chapters:

//*[((name()='h1' or name()='h2') and re:test(., 'chapter|book|section|part\s+', 'i')) or @class = 'chapter']

Bu ifade nispeten karmaşık, çünkü bir takım yaygın durumları aynı anda işlemeye çalışıyor. Anlamı ise calibre’nin bölümlerin başını ya içinde (chapter, book, section veya part) kelimelerinden herhangi birini veya class=”chapter” özniteliğini içeren <h1> veya <h2> etiketleri olarak varsayacağıdır.

İlgili bir seçenek de Chapter işareti, calibre’nin bir bölüm algıladığında ne yapacağını kontrol etmenizi sağlayan seçeneği. Ön tanımlı olarak, bölümden önce bir satır sonu ekleyecektir. Bunun yerine veya satır sonundan sonra kurallı bir satır ekleyebilir. Başka bir şey de yaptıramazsınız.

Sayfa sonlarını algılamak için ön tanımlı ayar:

//*[name()='h1' or name()='h2']

bu da calibre’nin ön tanımlı olarak her <h1> ve <h2> etiketinden önce sayfa sonu ekleyeceği demek oluyor.

Not

Varsayılan ifadeler dönüştürdüğünüz girdi biçimine göre değişebilir.

Muhtelif

Bu bölümde bir kaç seçenek daha var.

Metadata’yı kitabın başına bir sayfa olarak ekle

One of the great things about calibre is that it allows you to maintain very complete metadata about all of your books, for example, a rating, tags, comments, etc. This option will create a single page with all this metadata and insert it into the converted e-book, typically just after the cover. Think of it as a way to create your own customised book jacket.

İlk resmi kaldır

Bazen, dönüştürdüğünüz kaynak belge kapağı ayrı bir kapak olarak değil, kitabın bir parçası olarak bulundurur. Calibre’de ayrıca bir kapak belirtirseniz, dönüştürülen kitabın iki kapak sayfası olur. Bu seçenek basitçe belgedeki ilk resmi kaldırır, dönüştürülen kitabın tek bir kapağı, calibre tarafından belirtilen kapağı olmasını sağlar.

İçindekiler

When the input document has a Table of Contents in its metadata, calibre will just use that. However, a number of older formats either do not support a metadata based Table of Contents, or individual documents do not have one. In these cases, the options in this section can help you automatically generate a Table of Contents in the converted e-book, based on the actual content in the input document.

Not

Using these options can be a little challenging to get exactly right. If you prefer creating/editing the Table of Contents by hand, convert to the EPUB or AZW3 formats and select the checkbox at the bottom of the Table of Contents section of the conversion dialog that says Manually fine-tune the Table of Contents after conversion. This will launch the ToC Editor tool after the conversion. It allows you to create entries in the Table of Contents by simply clicking the place in the book where you want the entry to point. You can also use the ToC Editor by itself, without doing a conversion. Go to Preferences → Interface → Toolbars and add the ToC Editor to the main toolbar. Then just select the book you want to edit and click the ToC Editor button.

İlk seçenek Otomatik oluşturulmuş İçindekileri kullanmaya zorla. Bunu işaretleyerek calibre’nin girdi belgesinde bulunan herhangi bir İçindekiler bölümü yerine otomatik üretileni kullanmaya zorlamasını sağlayabilirsiniz.

Otomatik üretilen İçindekiler bölümünün çalışma şekli, calibre’nin algılanan tüm bölümleri üretilen içindekiler bölümüne eklemeye çalışması iledir. Bölüm algılamayı nasıl özelleştirebileceğinizi yukarıdaki Structure detection kısmı ile öğrenebilirsiniz. Algılanan bölümlerin üretilen içindekiler bölümünde yer almasını istemiyorsanız, Algılanan bölümleri ekleme seçeneğini işaretleyin.

If less than the Chapter threshold number of chapters were detected, calibre will then add any hyperlinks it finds in the input document to the Table of Contents. This often works well: many input documents include a hyperlinked Table of Contents right at the start. The Number of links option can be used to control this behavior. If set to zero, no links are added. If set to a number greater than zero, at most that number of links is added.

calibre otomatik olarak kopya girdileri İçindekiler bölümünden çıkarır. Fakat istenmeyen başka girdiler de varsa, TOC Filtresi seçeneği ile filtreleyebilirsiniz. Bu, üretilen içindekilerdeki girdi başlıklarıyla eşleşecek bir düzenli ifadedir. Bulunan her eşleşme kaldırılacaktır. Örneğin, tüm “Sonraki” veya “Önceki” girdi başlıklarını kaldırmak için şunu kullanabilirsiniz:

Next|Previous

Seviye 1,2,3 TOC`seçenekleri gelişmiş çok seviyeli bir İçindekiler oluşturmanıza izin verir. Bunlar dönüştürme hattınca üretilen ara XHTML’de eşleşen XPath ifadeleridir. Bu XHTML’ye nasıl erişeceğinizi öğrenmek için :ref:`conversion-introduction a göz atın. Her seçeneğin yanında temel XPath ifadeleri oluşturmanıza yardım edecek bir sihirbaz açan düğmeler bulunur. Şu basit örnek bu seçenekleri nasıl kullanacağınızı gösterir.

Aşağıdakine benzer bir XHTML üreten bir girdi belgeniz olduğunu farz edin:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Sample document</title>
</head>
<body>
<h1>Chapter 1</h1>
...
<h2>Section 1.1</h2>
...
<h2>Section 1.2</h2>
...
<h1>Chapter 2</h1>
...
<h2>Section 2.1</h2>
...
</body>
</html>

Seçenekleri şu şekilde ayarlıyoruz:

Level 1 TOC : //h:h1
Level 2 TOC : //h:h2

Bu işlem aşağıdaki gibi görünen, otomatik üretilmiş iki seviyeli bir İçindekilere sebep olur:

Chapter 1
Section 1.1
Section 1.2
Chapter 2
Section 2.1

Uyarı

Not all output formats support a multi level Table of Contents. You should first try with EPUB output. If that works, then try your format of choice.

HTML girdi belgelerini dönüştürürken resimler bölüm başlığı olarak kullanılıyor

Suppose you want to use an image as your chapter title, but still want calibre to be able to automatically generate a Table of Contents for you from the chapter titles. Use the following HTML markup to achieve this:

<html>
<body>
<h2>Chapter 1</h2>
<p>chapter 1 text...</p>
<h2 title="Chapter 2"><img src="chapter2.jpg" /></h2>
<p>chapter 2 text...</p>
</body>
</html>

Seviye 1 TOC ayarını //h:h2 yapın. İkinci bölüm için, calibre başlığı etiketin metni olmadığından <h2> etiketindeki title özniteliğinden alır.

İçindekiler bölümündeki girdilere metin temin etmek için etiket öznitelikleri kullanılıyor

Kısmen uzun bölüm başlıklarınız varsa ve İçindekiler bölümünde kısaltılmış hallerini istiyorsanız, bu iş için başlık özniteliğini kullanabilirsiniz, örneğin:

<html>
<body>
<h2 title="Chapter 1">Chapter 1: Some very long title</h2>
<p>chapter 1 text...</p>
<h2 title="Chapter 2">Chapter 2: Some other very long title</h2>
<p>chapter 2 text...</p>
</body>
</html>

Seviye 1 TOC ayarını //h:h2/@title yapın. calibre etiket içindeki metni kullanmak yerine, başlığı <h2> etiketlerindeki title değerinden alacaktır. XPath ifadesinde sondaki /@title a dikkat edin, bu biçimi kullanarak calibre’ye metni istediğiniz öznitelikten almasını söyleyebilirsiniz.

How options are set/saved for conversion

Calibre’de dönüştürme işlemlerinin ayarlanabileceği iki yer vardır. Birincisi Seçenekler->Dönüştürme. Bu seçenekler dönüştürme için varsayılan ayarlardır. Her kitap dönüştürmek istediğinizde, ön tanımlı olarak burada bulunan ayarlar kullanılır.

You can also change settings in the conversion dialog for each book conversion. When you convert a book, calibre remembers the settings you used for that book, so that if you convert it again, the saved settings for the individual book will take precedence over the defaults set in Preferences. You can restore the individual settings to defaults by using the Restore defaults button in the individual book conversion dialog. You can remove the saved settings for a group of books by selecting all the books and then clicking the Edit metadata button to bring up the bulk metadata edit dialog, near the bottom of the dialog is an option to remove stored conversion settings.

When you bulk convert a set of books, settings are taken in the following order (last one wins):

- Seçenekler->Dönüştürme menüsünde ayarlanan varsayılanlardan

- From the saved conversion settings for each book being converted (if any). This can be turned off by the option in the top left corner of the Bulk conversion dialog.

- Toplu dönüştürme iletişim penceresindeki ayarlardan

Note that the final settings for each book in a Bulk conversion will be saved and re-used if the book is converted again. Since the highest priority in Bulk Conversion is given to the settings in the Bulk conversion dialog, these will override any book specific settings. So you should only bulk convert books together that need similar settings. The exceptions are metadata and input format specific settings. Since the Bulk conversion dialog does not have settings for these two categories, they will be taken from book specific settings (if any) or the defaults.

Not

Herhangi bir dönüştürmede kullanılan ayarları görmek için aşağı sağ köşedeki dönen simgeye tıklayıp dönüştürme işine çift tıklayabilirsiniz. Bu işlem kullanılan ayarları üst kısma yakın bir yerde gösteren dönüştürme kaydını getirecektir.

Biçime özel ipuçları

Here you will find tips specific to the conversion of particular formats. Options specific to particular format, whether input or output are available in the conversion dialog under their own section, for example TXT input or EPUB output.

Microsoft Word belgelerini dönüştür
calibre can automatically convert .docx files created by Microsoft Word 2007 and newer. Just add the file to calibre and click convert.

Not

There is a demo .docx file [https://calibre-ebook.com/downloads/demos/demo.docx] that demonstrates the capabilities of the calibre conversion engine. Just download it and convert it to EPUB or AZW3 to see what calibre can do.

calibre will automatically generate a Table of Contents based on headings if you mark your headings with the Heading 1, Heading 2, etc. styles in Microsoft Word. Open the output e-book in the calibre E-book viewer and click the Table of Contents button to view the generated Table of Contents.

Eski .doc dosyaları

For older .doc files, you can save the document as HTML with Microsoft Word and then convert the resulting HTML file with calibre. When saving as HTML, be sure to use the “Save as Web Page, Filtered” option as this will produce clean HTML that will convert well. Note that Word produces really messy HTML, converting it can take a long time, so be patient. If you have a newer version of Word available, you can directly save it as .docx as well.

Another alternative is to use the free LibreOffice. Open your .doc file in LibreOffice and save it as .docx, which can be directly converted in calibre.

TXT belgelerini dönüştür

TXT belgelerinin kalın, yatık yazı vs. gibi iyi tanımlanmış biçimleme şekilleri ya da paragraflar, başlıklar, kısımlar gibi belge yapıları yoktur, ama çoğunlukça kullanılan bir takım genellemeler kullanılmaktadır. Varsayılan olarak calibre doğru biçimleme ve işaretlemeleri bu genellemelere bakarak otomatik yapmaya çalışır.

TXT girdisi paragrafların nasıl algılanacağı ayrımına varmak için bir takım seçeneği destekler.

Paragraph style: Auto

Paragrafların nasıl tanımlandığını otomatik olarak anlamak için metin dosyalarını analiz eder. Bu seçeneke çoğunlukla iyi çalışır, beklenmedik sonuçlarla karşılaşırsanız elle ayarlanan seçeneklerden birini deneyin.

Paragraph style: Block

Bir ya da fazla boş satırın paragraf sınırı olduğunu varsayar:

This is the first.

This is the
second paragraph.

Paragraph style: Single

Her satırın bir paragraf olduğunu varsayar:

This is the first.
This is the second.
This is the third.

Paragraph style: Print

Her paragrafın bir girintiyle başladığını varsayar (bir tab veya 2+ boşluk). Paragraflar sonraki satır girintiyle başladığında sona erer:

This is the
first.
This is the second.

This is the
third.

Paragraph style: Unformatted

Belgenin bir biçimi olmadığı, ama alt satır sonu kullandığı varsayılır. Noktalama işaretleri ve ortalama satır uzunluğu kullanılarak paragraflar tekrar oluşturulmaya çalışılır.

Formatting style: Auto

Kullanılan biçimlendirme işaretçileri türü algılanmaya çalışılır. İşaretçi kullanılmamışsa sezgisel biçimleme uygulanır.

Formatting style: Heuristic

Analyzes the document for common chapter headings, scene breaks, and italicized words and applies the appropriate HTML markup during conversion.

Formatting style: Markdown

calibre also supports running TXT input though a transformation preprocessor known as Markdown. Markdown allows for basic formatting to be added to TXT documents, such as bold, italics, section headings, tables, lists, a Table of Contents, etc. Marking chapter headings with a leading # and setting the chapter XPath detection expression to “//h:h1” is the easiest way to have a proper table of contents generated from a TXT document. You can learn more about the Markdown syntax at daringfireball [https://daringfireball.net/projects/markdown/syntax].

Formatting style: None

Applies no special formatting to the text, the document is converted to HTML with no other changes.

PDF belgelerini dönüştür

PDF documents are one of the worst formats to convert from. They are a fixed page size and text placement format. Meaning, it is very difficult to determine where one paragraph ends and another begins. calibre will try to unwrap paragraphs using a configurable, Line un-wrapping factor. This is a scale used to determine the length at which a line should be unwrapped. Valid values are a decimal between 0 and 1. The default is 0.45, just under the median line length. Lower this value to include more text in the unwrapping. Increase to include less. You can adjust this value in the conversion settings under PDF Input.

Also, they often have headers and footers as part of the document that will become included with the text. Use the Search and replace panel to remove headers and footers to mitigate this issue. If the headers and footers are not removed from the text it can throw off the paragraph unwrapping. To learn how to use the header and footer removal options, read Calibre’de kurallı ifadelerin kullanımına dair her şey.

PDF girdisinin bazı kısıtlamaları:

- Karmaşık, çok-sütunlu, ve resim temelli belgeler desteklenmez.

- Belgeden vektör resim ve tablo çıkarma işlemi de desteklenmez.

- Bazı PDF’ler ll veya ff veya fi vs. temsil etmek için özel kabartmalar kullanırlar. Bunların dönüştürülmesinin çalışıp çalışmayacağı PDF içinde nasıl temsil edildiklerine bağlıdır.

- Bağlantılar ve İçindekiler desteklenmiyor

- PDFs that use embedded non-Unicode fonts to represent non-English characters will result in garbled output for those characters

- Bazı PDF’ler arkasında OCRed metin bulunan o sayfanın fotoğrafından yapılmışlardır. Böyle durumlarda calibre PDF’i görüntülediğinizde göreceğinizden çok farklı sonuç verecek olan OCRed metni kullanır

- Sağdan sola yazılan diller ve matematik karakter dizisi gibi karmaşık metin görüntülemek için kullanılan PDF’ler düzgün dönüştürülmeyecektir

Tekrar üstünden geçelim PDF gerçekten, ama gerçekten kötü bir girdi formatıdır. İlla PDF kullanmanız gerekiyorsa, girdi PDF’ine bağlı olarak idare eder ile kullanılamaz arasında bir çıktı için hazırlıklı olun.

Comic book collections

A comic book collection is a .cbc file. A .cbc file is a ZIP file that contains other CBZ/CBR files. In addition the .cbc file must contain a simple text file called comics.txt, encoded in UTF-8. The comics.txt file must contain a list of the comics files inside the .cbc file, in the form filename:title, as shown below:

one.cbz:Chapter One
two.cbz:Chapter Two
three.cbz:Chapter Three

Böylece .cbc dosyası şunu içerecektir:

comics.txt
one.cbz
two.cbz
three.cbz

calibre will automatically convert this .cbc file into a e-book with a Table of Contents pointing to each entry in comics.txt.

EPUB gelişmiş biçimleme tanıtımı

Various advanced formatting for EPUB files is demonstrated in this demo file [https://calibre-ebook.com/downloads/demos/demo.epub]. The file was created from hand coded HTML using calibre and is meant to be used as a template for your own EPUB creation efforts.

The source HTML it was created from is available demo.zip [https://calibre-ebook.com/downloads/demos/demo.zip]. The settings used to create the EPUB from the ZIP file are:

ebook-convert demo.zip .epub -vv --authors "Kovid Goyal" --language en --level1-toc '//*[@class="title"]' --disable-font-rescaling --page-breaks-before / --no-default-epub-cover

Bu dosya EPUB’ın potansiyelini araştırdığından, calibre’nin yerleşik okuyucusu dışındaki bir çok okuyucuda burdaki gelişmiş biçimlemeler çalışmayacaktır.

ODT belgelerini dönüştür
calibre ODT (OpenDocument Metni) dosyalarını doğrudan dönüştürebilir. Belgenizi biçimlendirmek için biçemler kullanmalı ve doğrudan biçimleme kullanmaktan kaçınmalısınız. Belgenize resim eklerken, resimleri paragrafa bağlamalısınız, bir sayfaya bağlanmış resimlerin hepsi dönüştürmenin başında yer alacaktır.

To enable automatic detection of chapters, you need to mark them with the built-in styles called Heading 1, Heading 2, …, Heading 6 (Heading 1 equates to the HTML tag <h1>, Heading 2 to <h2>, etc). When you convert in calibre you can enter which style you used into the Detect chapters at box. Example:

- If you mark Chapters with style Heading 2, you have to set the ‘Detect chapters at’ box to //h:h2

- For a nested TOC with Sections marked with Heading 2 and the Chapters marked with Heading 3 you need to enter //h:h2|//h:h3. On the Convert - TOC page set the Level 1 TOC box to //h:h2 and the Level 2 TOC box to //h:h3.

İyi bilinen belge özellikleri (Başlık, Anahtar kelimeler, Açıklama, Oluşturan) tanınır ve calibre ilk resmi (çok küçük olmayan, iyi en boy oranına sahip) kapak resmi yapar.

Ayrıca ODT belgenizde Evet olarak ayarladığınızda (Dosya->Özellikler->Özel Özellikler) etkinleşen özel bir özellik olan opf.metadata (‘Evet veya Hayır’ biçimli) gelişmiş özellik dönüştürme kipi bulunmaktadır. Bu özellik calibre tarafından algılanırsa, aşağıdaki özellikler tanınır (opf.authors belgeyi oluşturanı ezer):

opf.titlesort
opf.authors
opf.authorsort
opf.publisher
opf.pubdate
opf.isbn
opf.language
opf.series
opf.seriesindex

Buna ek olarak, ODT’de kapak resmi olmasını istediğiniz resmi opf.cover olarak isimlendirebilirsiniz (sağ tık, Resim->Seçenekler->Ad). Bu isimde bir resim bulunmazsa, ‘zeki’ kipi kullanılır. Bazı çıktı biçimlerinde kapak algılama iki kapakla sonlanabileceğinden, işlem paragrafı belgeden çıkaracaktır (yalnızca tek içerik kapak ise!). Ama bu yalnızca adlandırılan resimle çalışır!

Kapak algılamayı kapatmak için opf.nocover (‘Evet veya Hayır’ türünde) gelişmiş kipte Evet olarak ayarlayabilirsiniz.

PDF’e dönüştürme

The first, most important, setting to decide on when converting to PDF is the page size. By default, calibre uses a page size of “U.S. Letter”. You can change this to another standard page size or a completely custom size in the PDF Output section of the conversion dialog. If you are generating a PDF to be used on a specific device, you can turn on the option to use the page size from the output profile instead. So if your output profile is set to Kindle, calibre will create a PDF with page size suitable for viewing on the small Kindle screen.

Başlıklar ve Altbilgiler

Başlık ve altbilgi şablonları belirleyerek PDF’in her sayfasına keyfi başlık ve alt bilgi ekleyebilirsiniz. Şablonlar başlık ve altbilgi alanlarında gerçeklenen HTML kodlarından başka bir şey değildir. Örneğin her sayfanın altında ortalanmış olarak sayfa numaraları göstermek için, aşağıdaki altbilgi şablonunu kullanın:

<footer><div style="margin: auto; color: green">_PAGENUM_</div></footer>

calibre will automatically replace _PAGENUM_ with the current page number. You can even put different content on even and odd pages, for example the following header template will show the title on odd pages and the author on even pages:

<header style="justify-content: flex-end">
<div class="even-page">_AUTHOR_</div>
<div class="odd-page"><i>_TITLE_</i></div>
</header>

calibre will automatically replace _TITLE_ and _AUTHOR_ with the title and author of the document being converted. Setting justify-content to flex-end will cause the text to be right aligned.

You can also display text at the left and right edges and change the font size, as demonstrated with this header template:

<header style="justify-content: space-between; font-size: smaller">
<div>_TITLE_</div>
<div>_AUTHOR_</div>
</header>

Bu başlığı solda, yazarı sağda ana metinden küçük bir boyutta gösterir.

Mevcut kısmı da şablonlarda aşağıda gösterildiği gibi kullanabilirsiniz:

<header><div>_SECTION_</div></header>

_SECTION_ is replaced by whatever the name of the current section is. These names are taken from the metadata Table of Contents in the document (the PDF Outline). If the document has no table of contents then it will be replaced by empty text. If a single PDF page has multiple sections, the first section on the page will be used. Similarly, there is a variable named _TOP_LEVEL_SECTION_ that can be used to get the name of the current top-level section.

You can even use JavaScript inside the header and footer templates, for example, the following template will cause page numbers to start at 4 instead of 1:

<footer>
<div></div>
<script>document.currentScript.parentNode.querySelector("div").innerHTML = "" + (_PAGENUM_ + 3)</script>
</footer>

In addition there are some more variables you can use in the headers and footers, documented below:

- _TOTAL_PAGES_ - total number of pages in the PDF file, useful for implementing a progress counter, for example.

- _TOP_LEVEL_SECTION_PAGES_ - total number of pages in the current top level section

- _TOP_LEVEL_SECTION_PAGENUM_ - the page number of the current page within the current top level section

Not

When adding headers and footers make sure you set the page top and bottom margins to large enough values, under the PDF Output section of the conversion dialog.

Yazdırılabilir İçindekiler

Ayrıca PDF sonuna her kısmın sayfa numaralarını içeren y azdırılabilir bir İçindekiler ekleyebilirsiniz. PDF’i sayfaya yazdırmaya niyetiniz varsa çok faydalıdır. PDF’i elektronik cihazda kullanacaksanız, PDF Özeti bu işlevselliği sağlar ve ön tanımlı olarak üretilir.

You can customize the look of the generated Table of contents by using the Extra CSS conversion setting under the Look & feel part of the conversion dialog. The default CSS used is listed below, simply copy it and make whatever changes you like.

.calibre-pdf-toc table { width: 100%% }

.calibre-pdf-toc table tr td:last-of-type { text-align: right }

.calibre-pdf-toc .level-0 {
font-size: larger;
}

.calibre-pdf-toc .level-1 td:first-of-type { padding-left: 1.4em }
.calibre-pdf-toc .level-2 td:first-of-type { padding-left: 2.8em }

Custom page margins for individual HTML files

If you are converting an EPUB or AZW3 file with multiple individual HTML files inside it and you want to change the page margins for a particular HTML file you can add the following style block to the HTML file using the calibre E-book editor:

<style>
@page {
margin-left: 10pt;
margin-right: 10pt;
margin-top: 10pt;
margin-bottom: 10pt;
}
</style>

Then, in the PDF output section of the conversion dialog, turn on the option to Use page margins from the document being converted. Now all pages generated from this HTML file will have 10pt margins.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- E-kitap düzenleme

E-kitap düzenleme
calibre EPUB ve AZW3 (Kindle) biçimlerinde kitapları düzenlemeye yarayan dahili bir e-kitap düzenleyicisi içerir. Düzenleyici kitap dosyalarında dahili olarak kullanılan HTML ve CSS’i ve değişiklik yaptıkça güncellenen bir canlı ön izlemeyi gösterir. Ayrıca genel temizlik ve düzeltme işleri için çeşitli otomatize araç da içerir.

Bu düzenleyiciyi calibre’de herhangi bir kitaba sağ tıklayıp Kitabı düzenle seçerek kullanabilirsiniz.

İçindekiler

Temel iş akışı

The File browser

- Dosyaları yeniden adlandırma

- Dosyaları birleştirme

- Metin dosyası sırası değiştirme

- Kapağı işaretleme

- Dosyaları siliyor

- Exporting files

- Yeni resim/yazı tipi/vs. ekleme veya yeni boş dosyalar oluşturma

- Dosyaları yer değiştirme

- Biçim sayfalarını HTML dosyalarına etkin biçimde bağlama

Search & replace

- Kaydedilmiş aramalar

- İşlev kipi

- Search ignoring HTML tags

Otomatik araçlar

- Editing the Table of Contents

- Checking the book

- Adding a cover

- Kaynak yazı tipleri katıştırılıyor

- Katıştırılmış yazı tiplerinin alt kümelere ayrılması

- Noktalamanın daha akıllı hale getirilmesi

- Transforming CSS properties

- Kullanılmayan CSS kurallarını kaldırma

- Fixing HTML

- Dosyaların güzelleştirilmesi

- Inserting an inline Table of Contents

- Setting Semantics

- Filtering style information

- Upgrading the book’s internals

Kontrol noktaları

The Live preview panel

- HTML dosyalarının ayrılması

Canlı CSS paneli

Miscellaneous tools

- The Table of Contents view

- Kitaptaki kelimelerin yazım denetimi

- Özel karakter eklemek

- Kod denetleme görünümü

- Checking external links

- Downloading external resources

- Arranging files into folders by type

- Diğer e-kitap biçimlerindeki dosyaları EPUB olarak içe aktarma

- Raporlar aracı

Kod düzenleyicideki özel özellikler

- Sözdizim vurgulama

- İçeriğe duyarlı yardım

- Otomatik tamamlama

- Kesitler

Temel iş akışı

Not

A video tour of the calibre E-book editor is available here [https://calibre-ebook.com/demo#tutorials].

Kitap düzenleme aracıyla bir kitabı ilk defa açtığınızda, solda bir dosya listesiyle karşılanırsınız. Bunlar kitap içeriğini oluşturan HTML dosyaları, biçem sayfaları, resimler vs. dir. Bir dosyayı düzenlemek için basitçe sağ tıklayın. Bir kaç küçük düzeltmeden başka bir şeyler yapacaksanız, HTML eğitimi [http://html.net/tutorials/html/] ve CSS Eğitimi [http://html.net/tutorials/css/] bilmeniz gerektiğini unutmayın.

As you make changes to the HTML or CSS in the editor, the changes will be previewed, live, in the preview panel to the right. When you are happy with how the changes you have made look, click the Save button or use File → Save to save your changes into the e-book.

One useful feature is Checkpoints. Before you embark on some ambitious set of edits, you can create a checkpoint. The checkpoint will preserve the current state of your book, then if in the future you decide you don’t like the changes you have made to you can go back to the state when you created the checkpoint. To create a checkpoint, use Edit → Create checkpoint. Checkpoints will also be automatically created for you whenever you run any automated tool like global search and replace. The checkpointing functionality is in addition to the normal undo/redo mechanism when editing individual files. Checkpoints are needed for when changes are spread over multiple files in the book.

Kitap düzenleme için temel akış bu şekildedir – Bir dosya aç, değişiklikler yap, ön izle ve kaydet. Bu kılavuzun devamı belirli görevleri efektif yapabilmeniz için mevcut çeşitli araçları ve özellikleri tartışacaktır.

The File browser

The File browser gives you an overview of the various files inside the book you are editing. The files are arranged by category, with text (HTML) files at the top, followed by stylesheet (CSS) files, images and so on. Simply double click on a file to start editing it. Editing is supported for HTML, CSS and image files. The order of text files is the same order that they would be displayed in, if you were reading the book. All other files are arranged alphabetically.

By hovering your mouse over an entry, you can see its size, and also, at the bottom of the screen, the full path to the file inside the book. Note that files inside e-books are compressed, so the size of the final book is not the sum of the individual file sizes.

Many files have special meaning, in the book. These will typically have an icon next to their names, indicating the special meaning. For example, in the picture to the left, you can see that the files cover_image.jpg and titlepage.xhtml have the icon of a cover next to them, this indicates they are the book cover image and titlepage. Similarly, the content.opf file has a metadata icon next to it, indicating the book metadata is present in it and the toc.ncx file has a T icon next to it, indicating it is the Table of Contents.

Tekli dosyalar üstünde bir çok hareketi sağ tıklayarak sağlayabilirsiniz.

Dosyaları yeniden adlandırma

Tek bir dosyayı sağ tıklayarak ve Yeniden adlandır seçerek yeniden adlandırabilirsiniz. Bir dosyayı yeniden adlandırmak kitapta ona olan tüm bağları ve referansları günceller. Yani tek yapmanız gereken yeni ismi sağlamaktır, gerisini calibre halleder.

You can also bulk rename many files at once. This is useful if you want the files to have some simple name pattern. For example you might want to rename all the HTML files to have names Chapter-1.html, Chapter-2.html and so on. Select the files you want bulk renamed by holding down the Shift or Ctrl key and clicking the files. Then right click and select Bulk rename. Enter a prefix and what number you would like the automatic numbering to start at, click OK and you are done. The bulk rename dialog also lets you rename files by the order they appear in the book instead of the order you selected them in, useful, for instance to rename all images by the order they appear.

Finally, you can bulk change the file extension for all selected files. Select multiple files, as above, and right click and choose Change the file extension for the selected files.

Dosyaları birleştirme

Sometimes, you may want to merge two HTML files or two CSS files together. It can sometimes be useful to have everything in a single file. Be wary, though, putting a lot of content into a single file will cause performance problems when viewing the book in a typical e-book reader.

To merge multiple files together, select them by holding the Ctrl key and clicking on them (make sure you only select files of one type, either all HTML files or all CSS files and so on). Then right click and select merge. That’s all, calibre will merge the files, automatically taking care of migrating all links and references to the merged files. Note that merging files can sometimes cause text styling to change, since the individual files could have used different stylesheets.

You can also select text files and then drag and drop the text files onto another text file to merge the dropped text files into the target text file.

Metin dosyası sırası değiştirme

You can re-arrange the order in which text (HTML) files are opened when reading the book by simply dragging and dropping them in the File browser or clicking on the file to move and then pressing the Ctrl+Shift modifiers with the Up, Down, Home or End keys. For the technically inclined, this is called re-ordering the book spine.

Note that you have to drop the items between other items, not on top of them, this can be a little fiddly until you get used to it. Dropping on top of another file will cause the files to be merged.

Kapağı işaretleme

E-books typically have a cover image. This image is indicated in the File browser by the icon of a brown book next to the image name. If you want to designate some other image as the cover, you can do so by right clicking on the file and choosing Mark as cover.

Ek olarak, EPUB dosyalarının bir başlık sayfası kavramı vardır. Başlık sayfası kitabın başlık sayfası/kapağı olarak davranan bir HTML dosyasıdır. EPUB düzenlerken bir HTML dosyasını başlık sayfası olarak sağ tıklayarak ayarlayabilirsiniz. Seçtiğiniz dosyanın yalnızca kapak bilgisi içerdiğinden emin olun. Başka bir içeriği olursa, ilk bölüm gibi, kullanıcı calibre’de bu EPUB’I başka bir biçime dönüştürdüğünde bu içerik silinir. Bunun sebebi dönüştürme sırasında, calibre’nin işaretli başlık sayfasının sadece kapak bilgisi içerdiğini var saymasındandır.

Dosyaları siliyor

Dosyaları sağ tıklayarak ya da seçip Delete tuşuna basarak silebilirsiniz. Bir dosyayı silmek OPF dosyasından ona olan tüm referansları da siler. Ancak başka yerlerdeki referanslar silinmez, Kitabı Kontrol Et aracını kullanarak bunları kolayca silebilir/yer değiştirebilirsiniz.

Exporting files

Dosyaları kitaptan bilgisayarınızdaki başka bir yere aktarabilirsiniz. Dosya üstünde özel olarak başka araçlarla çalışmak istiyorsanız faydalı olabilir. Bu işlem için dosyaya sağ tıklayın ve :guilabel:`Dışa aktar`ı seçin.

Dışa aktarılan dosya üstünde çalışmayı bitirdiğinizde, kitaba yeniden, dosyaya sağ tıklayıp :guilabel:`Dosya ile yer değiştir…`i seçerek geri aktarabilirsiniz, bu da kitaptaki dosyayı daha önce dışa aktardığınız dosyayla değiştirmenize olanak verir.

You can also copy files between multiple editor instances. Select the files you want to copy in the File browser, then right click and choose, Copy selected files to another editor instance. Then, in the other editor instance, right click in the File browser and choose Paste file from other editor instance.

Yeni resim/yazı tipi/vs. ekleme veya yeni boş dosyalar oluşturma

You can add a new image, font, stylesheet, etc. from your computer into the book by clicking File → New file. This lets you either import a file by clicking the Import resource file button or create a new blank HTML file or stylesheet by simply entering the file name into the box for the new file.

Ayrıca birden çok dosyayı tek seferde Dosya->Dosya içe aktar ile kitaba aktarabilirsiniz.

Dosyaları yer değiştirme

Kitapta mevcut dosyaları sağ tıklayıp yer değiştiri seçerek kolayca değiştirebilirsiniz. Değiştirilen dosyanın farklı bir adı varsa, bu işlem tüm referansları ve bağlantıları da günceller.

Biçim sayfalarını HTML dosyalarına etkin biçimde bağlama

As a convenience, you can select multiple HTML files in the File browser, right click and choose Link stylesheets to have calibre automatically insert the <link> tags for those stylesheets into all the selected HTML files.

Search & replace

Edit book has a very powerful search and replace interface that allows you to search and replace text in the current file, across all files and even in a marked region of the current file. You can search using a normal search or using regular expressions. To learn how to use regular expressions for advanced searching, see Calibre’de kurallı ifadelerin kullanımına dair her şey.

Aramayı başlatın ve Ara → Bul/değiştir menü girdisi ile yer değiştirin (HTML veya CSS dosyası düzenliyor olmalısınız).

Bulmak istediğiniz metni Arama kutusuna yazın ve Yer değiştir kutusuna da değiştirilecek halini. Sonraki eşleşmeyi bulmak için uygun düğmelere tıklayabilir, mevcut eşleşmeyi veya tüm eşleşmeleri değiştirebilirsiniz.

Kutunun altındaki açılır menülerden, aramanın mevcut dosya, tüm metin dosyaları, tüm biçim dosyaları veya tüm dosyalar üzerinde çalışmasını sağlayabilirsiniz. Ayrıca arama kipini normal (karakter dizisi) arama veya düzenli ifade arama olarak seçebilirsiniz.

Bir arama için tüm eşleşmeleri Ara → Hepsini say ile sayabilirsiniz. Sayma açılır kutudan hangi dosyaları/bölgeleri seçtiyseniz onlar üzerinde çalışır.

Ayrıca o an açık düzenleyicide belirli bir satıra Ara → Satıra git ile gidebilirsiniz.

Not

Unutmayın, arama ve yer değiştirmenin tüm gücünü toplamak için, düzenli ifadeler kullanmanız gerekir. Bknz Calibre’de kurallı ifadelerin kullanımına dair her şey.

Kaydedilmiş aramalar

You can save frequently used search/replace expressions (including function mode expressions) and reuse them multiple times. To save a search simply right click in the Find box and select Save current search.

You can bring up the saved searches via Search → Saved searches. This will present you with a list of search and replace expressions that you can apply. You can even select multiple entries in the list by holding down the Ctrl key while clicking so as to run multiple search and replace expressions in a single operation.

İşlev kipi

Function mode allows you to write arbitrarily powerful Python functions that are run on every Find/replace. You can do pretty much any text manipulation you like in function mode. For more information, see Function mode for Search & replace in the Editor.

Search ignoring HTML tags

There is also a dedicated tool for searching for text, ignoring any HTML tags in between. For example, if the book has the HTML Empahisis on a <i>word</i>. you can search for on a word and it will be found even though there is an <i> tag in the middle. Use this tool via the Search → Search ignoring HTML markup menu item.

Otomatik araçlar

Kitap düzenlenin yaygın görevlerle ilgili bir çok aracı vardır. Bunlara Araçlar menüsü ile erişebilirsiniz.

Editing the Table of Contents

İçindekileri kolayca düzenlemek için özel bir araç vardır. Araçlar → İçindekiler → İçindekileri Düzenle ile başlatabilirsiniz.

İçindekileri Düzenle aracı solda mevcut (eğer varsa) İçindekileri gösterir. Metnini değiştirmek için herhangi bir girdiye çift tıklayın. Ayrıca girdileri sağdaki düğmeleri kullanarak veya sürükle bırak yaparak yeniden düzenleyebilirsiniz.

Mevcut bir İçindekileri olmayan kitaplar için, bu araç size metinden otomatik İçindekiler oluşturmanız için çeşitli seçenekler sunar. Belgedeki başlıklardan, bağlantılardan, dosyalardan vs İçindekiler oluşturabilirsiniz.

Girdileri tıklayıp Bu girdinin gösterdiği konumu değiştir ile düzenleyebilirsiniz. Bu işlem kitabın küçük bir ön izlemesini açar, basitçe fare işaretçisini kitap görüntüleme paneli üzerinde hareket ettirin ve girdinin göstermesini istediğiniz noktaya tıklayın. Kalın bir yeil çizgi konumu gösterecektir. Konumdan memnun olduğunuzda OK tıklayın.

Checking the book

The Check book tool searches your book for problems that could prevent it working as intended on actual reader devices. Activate it via Tools → Check book.

Bulunan tüm problemler güzel, kullanımı kolay bir listede raporlanır. Listedeki herhangi bir girdiye tıklamak bu hata hakkında biraz yardım ve bu hatayı mümkünse otomatik düzeltme seçeneği sunar. Hataya çift tıklayarak hatanın bulunduğu noktayı düzenleyicide açabilir, kendiniz düzeltebilirsiniz.

Gerçekleştirilen kontrollerden bazıları:

- Bozuk HTML işaretçileri. İyi biçimlenmiş XML olarak ayrıştırılamayan tüm HTML işaretçileri raporlanır. Düzeltilmesi biçimlemenizin tüm bağlamda olması gibi çalıştığını garantiler. calibre bu hataları otomatik düzeltebilir, ama otomatik düzeltmenin beklenmedik etkileri olabilir, dikkatli kullanın. Her zaman olduğu gibi, otomatik düzeltmeden önce bir kontrol noktası oluşturulur ki kolayca geri dönebilin. Otomatik düzeltme işaretçilerin oldukça hata payı bırakan HTML5 algoritmasıyla ayrıştırılmasıyla, ardından XML’e dönüştürülmesiyle yapılır.

- Bozuk ya da bilinmeyen CSS biçimi. Geçerli olmayan veya CSS 2.1 standardında (CSS 3’den de bir kaç tane eklenerek) tanımlanmayan özellikler içeren herhangi bir CSS raporlanır. CSS tüm biçim dosyalarında, satır arası özniteliklerde ve HTML dosyalarındaki <style> etiketlerinde kontrol edilir.

- Kırık bağlantılar. Kitap içinde eksik ya da bulunmayan dosyaları gösteren bağlantılar varsa raporlanır.

- Referansı olmayan dosyalar. Başka bir dosya tarafından referans edilmeyen veya omurgada olmayan dosyalar raporlanır.

- OPF dosyalarında kopya omurga veya manifesto öğeleri, kırık idref’ler veya meta kapak etiketleri, eksik gerekli kısımlar vs.

- Kitabın okuyucu cihazlarda bozuk çalışmasına sabep olacak bilinen problemlere uyumluluk kontrolleri.

Adding a cover

Araçlar → Kapak ekle kullanarak kitaba kolaylıkla bir kapak ekleyebilirsiniz. Bu işlem mevcut bir resmi seçip kapak yapmanızı veya yeni bir resmi içe aktarıp kapak yapmanızı sağlar. EPUB dosyalarını düzenlerken, kapak için HTML kapsayıcı otomatik üretilir. Kitapta mevcut bir kapak bulunursa, yer değiştirilir. Araç ayrıca OPF’de kapak dosyalarını düzgünce işaretleme işini de otomatik halleder.

Kaynak yazı tipleri katıştırılıyor

Araçlar → Başvurulan fontları göm ile erişilen bu araç kitapta başvurulan tüm yazı tiplerini bulur ve gömülü değillerse, bilgisayarınızda arama yapar ve bulunursa kitaba gömer. Bu işlemi yapmadan ticari yazı tiplerini gömmek için gerekli izniniz olduğundan emin olun lütfen.

Katıştırılmış yazı tiplerinin alt kümelere ayrılması

Araçlar → Gömülü yazı tiplerini altkümele ile erişilen bu araç, kitaptaki tüm yazı tiplerini kitapta gerçekten mevcut olan kabartmaları içerecek şekilde eksiltir. Bu işlem genellikle yazı tipi dosyalarının boyutunu ~ 50% azaltır. Ancak yazı tipleri altkümelendikten sonra, daha önce altkümede mevcut olmayan bir yazı tipi eklerseniz, yazı tipi yeni metin için çalışmayacaktır. Yani bunu iş akışında son adım olarak yapın.

Noktalamanın daha akıllı hale getirilmesi

Düz metin tire işaretlerini, elipsleri, tırnakları, çift çizgileri, vs. tipografik olarak düzgün denklerine dönüştür. Algoritmanın bazen yanlış sonuç üretebileceğini not edin, özellikle daraltmanın başında tek tırnak işaretleri varken. Araçlar → Noktalama işaretlerini toparla ile erişlebilir.

Transforming CSS properties

Create rules to transform the styling of the book. For example, create a rule to convert all red text to green or to double the font size of all text in the book or make text of a certain font family italic, etc.

Creating the rules is simple, the rules follow a natural language format, that looks like:

- If the property color is red change it to green

- If the property font-size is any value multiply the value by 2

Accessed via Tools → Transform styles.

Kullanılmayan CSS kurallarını kaldırma

Remove all unused CSS rules from stylesheets and <style> tags. Some books created from production templates can have a large number of extra CSS rules that don’t match any actual content. These extra rules can slow down readers that need to process them all. Accessed via Tools → Remove unused CSS.

Fixing HTML

This tool simply converts HTML that cannot be parsed as XML into well-formed XML. It is very common in e-books to have non-well-formed XML, so this tool simply automates the process of fixing such HTML. The tool works by parsing the HTML using the HTML5 algorithm (the algorithm used in all modern browsers) and then converting the result into XML. Be aware that auto-fixing can sometimes have counter-intuitive results. If you prefer, you can use the Check Book tool discussed above to find and manually correct problems in the HTML. Accessed via Tools → Fix HTML.

Dosyaların güzelleştirilmesi

This tool is used to auto-format all HTML and CSS files so that they “look pretty”. The code is auto-indented so that it lines up nicely, blank lines are inserted where appropriate and so on. Note that beautifying also auto-fixes broken HTML/CSS. Therefore, if you don’t want any auto-fixing to be performed, first use the Check Book tool to correct all problems and only then run beautify. Accessed via Tools → Beautify all files.

Not

In HTML any text can have significant whitespace, via the CSS white-space directive. Therefore, beautification could potentially change the rendering of the HTML. To avoid this as far as possible, the beautify algorithm only beautifies block level tags that contain other block level tags. So, for example, text inside a <p> tag will not have its whitespace changed. But a <body> tag that contains only other <p> and <div> tags will be beautified. This can sometimes mean that a particular file will not be affected by beautify as it has no suitable block level tags. In such cases you can try different beautification tools, that are less careful, for example: HTML Tidy [https://infohound.net/tidy/].

Inserting an inline Table of Contents

Normally in e-books, the Table of Contents is separate from the main text and is typically accessed via a special Table of Contents button/menu in the e-book reading device. You can also have calibre automatically generate an inline Table of Contents that becomes part of the text of the book. It is generated based on the currently defined Table of Contents.

Bu aracı birden fazla kullanırsanız, her çalıştırmada bir öncekinde oluşturulan İçindekiler yer değiştirecektir. Araca Araçlar → İçindekiler → Satır Arası İçindekiler Ekle ile erişilebilir.

Setting Semantics

Bu araç EPUB dosyalarında anlamsal ayarlar yapmak için kullanılır. Anlamsallar basitçe, OPF dosyasında kitapta özel anlama sahip belirli konumları ayırt eden bağlantılardır. Önsöz, kapak, ithaf, içindekiler vs. tanımlamak için kullanabilirsiniz. Belirtmek istediğiniz anlamsal bilgi türünü seçin ve kitapta göstermesini istediğiniz noktayı seçin. Bu araca Araçlar → Anlamsal Ayarlar ile erişebilirsiniz.

Filtering style information

Bu araç tüm kitaptan belirtilen CSS biçem özelliklerini kolayca çıkarmak için kullanılabilir. Kaldırılmasını istediğiniz özellikleri sölyeyebilirsiniz, örneğin color, background-color, line-height ve bulundukları her yerden kaldırılırlar — biçem dosyaları, <style> etiketleri ve satır arası style öznitelikleri.

Upgrading the book’s internals

This tool can be used to upgrade the book’s internals, if possible. For instance it will upgrade EPUB 2 books to EPUB 3 books. The tool can be accessed via Upgrade book internals.

Kontrol noktaları

Checkpoints are a way to mark the current state of the book as “special”. You can then go on to do whatever changes you want to the book and if you don’t like the results, return to the checkpointed state. Checkpoints are automatically created every time you run any of the automated tools described in the previous section.

Düzenle → Kontrol noktası oluştur ile bir kontrol noktası oluşturabilirsiniz. Önceki bir kontrol noktasına da Düzenle → Geri al… ile dönebilirsiniz.

The check pointing functionality is in addition to the normal Undo/redo mechanism when editing individual files. Checkpoints are needed for when changes are spread over multiple files in the book or when you wish to be able to revert a large group of related changes as a whole.

Görünüm → Kontrol noktaları ile uygun kontrol noktası listesini görebilirsiniz. Kitabın mevcut durumunu belirtilen bir kontrol noktası ile E-kitapları karşılaştır aracı ile karşılaştırabilirsiniz – ilgilenilen kontrol noktasını seçip Karşılaştır düğmesine tıklayarak. Geri al düğmesi kitabı seçili kontrol noktasına kontrol noktası oluşturulduktan sonra yapılan tüm değişiklikleri geri alarak geri yükler.

The Live preview panel

The File preview gives you an overview of the various files inside The live preview panel shows you the changes you are making live (with a second or two of delay). As you edit HTML or CSS files, the preview panel is updated automatically to reflect your changes. As you move the cursor around in the editor, the preview panel will track its location, showing you the corresponding location in the book. Clicking in the preview panel, will cause the cursor in the editor to be positioned over the element you clicked. If you click a link pointing to another file in the book, that file will be opened in the edit and the preview panel, automatically.

Değişikliklerin canlı izlenmesi ve otomatik konum eş zamanlamayı – ön izleme paneli altındaki düğmelerle kapatabilirsiniz. Ön izleme panelinin canlı güncellemesi sadece düzenleyicide yazmadığınız zaman yapılır, böylece dikkatinizi dağıtmaz ve yavaşlatmaz, ön izlemenin gerçeklenmesini bekler.

Ön izleme paneli metnin görüntülendiğinde nasıl görüneceğini gösterir. Ancak ön izleme paneli kitabınızı gerçek bir okuma aygıtında test etmenin yerini tutmaz. Gerçek bir okuyucudan hem daha çok hem daha az kapasitelidir. Bir çok okuyucu aygıttan daha fazla hatalara ve bozuk işaretçilere toleransı vardır. Ayrıca size sayfa kenar boşluklarını, sayfa sonlarını ve yazı tipi örtüştüren gömülü yazı tiplerini göstermez. Kitap üstünde çalışırken ön izleme panelini kullanın, ama işiniz bittiğinide, gerçek bir cihaz ya da yazılım öykünücüsünde görüntüleyin.

Not

Ön izleme paneli yazıtipi dosyası içindeki yazı tipi ismi CSS @font-face kuralındaki isimle uyuşmazsa gömülü yazı tiplerini desteklemez. Bu tarz yazı tipi sorunlarını Kitabı Kontrol Et aracını kullanarak hızlıca bulup düzeltebilirsiniz.

HTML dosyalarının ayrılması

One, perhaps non-obvious, use of the preview panel is to split long HTML files. While viewing the file you want to split, click the Split mode button under the preview panel . Then simply move your mouse to the place where you want to split the file and click. A thick green line will show you exactly where the split will happen as you move your mouse. Once you have found the location you want, simply click and the split will be performed.

Dosyayı ayırmak dosyanın alt yarısını gösteren tüm bağlantıların ve referansların otomatik güncellenmesini ve ayrılan kısmın yeni dosya düzenleyicide açılmasını sağlar.

Tek bir HTML dosyasını birden çok konumdan otomatik olarak düzenleyicideki dosyaya sağ tıklayıp Birden fazla konumdan ayır seçerek ayırabilirsiniz. Bu işlem büyük bir dosyayı tüm başlık etiketlerinde veya belli sınıfta etikete sahip her yerde ayırma imkanı sağlar.

Canlı CSS paneli

Canlı CSS paneli o an düzenlediğiniz etikete uygulanabilir tüm biçim kurallarını gösterir. Etiket adı, düzenleyicideki satırı ile birlikte gösterilir, ardından eşleşme kuralları listesi gelir.

Herhangi bir etikete hangi biçim kurallarının uygulandığını görmek için çok iyi bir yöntemdir. Görünüm ayrıca tıklanabilir bağlantılar (mavi renkte) içerir, bu bağlantılar biçim kurallarında değişiklik yapmak isterseniz biçimin tanımlandığı noktaya doğrudan götürür. Doğrudan etikete uygulanan biçim kuralları ana etiketlerden miras alınan kurallarla birlikte gösterilir.

Panel ayrıca etiket için hesaplanan son biçimleri de gösterir. Daha yüksek öncelikli kurallarca önüne geçilen özellikler üzeri çizili olarak gösterilir.

Canlı CSS panelini Görünüm → Canlı CSS ile etkinleştirebilirsiniz.

Miscellaneous tools

Kitabı düzenlerken faydalı olabilecek bir kaç araç daha var.

The Table of Contents view

The Table of Contents view shows you the current table of contents in the book. Double clicking on any entry opens the place that entry points to in an editor. You can right click to edit the Table of Contents, refresh the view or expand/collapse all items. Access this view via View → Table of Contents.

Kitaptaki kelimelerin yazım denetimi

You can run a spelling checker via Tools → Check spelling.

Kelimeler kitapta kaç kere geçtikleri ve hangi dilden olduklarıyla gösterilirler. Dil bilgisi kitabın metadatasından ve HTML dosyalarındaki lang özniteliğinden alınır. Bu da birden çok dil içeren kitaplarda bile yazım denetiminin çalışmasına olanak verir. Örneğin, aşağıdaki HTML çıktısında, color kelimesi Amerikan İngilizcesiyle, colour kelimesi İngiliz İngilizcesiyle kontrol edilir:

<div lang="en_US">color <span lang="en_GB">colour</span></div>

Not

Bir kelimeye çift tıklayarak düzenleyicide sonraki konumunu vurgulayabilirsiniz. Kelimeyi elle düzenlemek istiyorsanız, veya hangi içerikte bulunduğunu görmek istiyorsanız faydalıdır.

Bir kelimeyi değiştirmek için, sağdaki önerilen yazımlardan birine çift tıklayın, veya kendi düzeltilmiş yazımınızı yazın ve Seçili kelimeyi değiştir düğmesine tıklayın. Bu işlem kelimeyi tüm kitapta değiştirir. Ayrıca ana kelime listesindeki bir kelimeye sağ tıklayarak sağ tık menüsünden kolayca değiştirebilirsiniz.

Yazım denetiminin mevcut oturum için bir kelimeyi göz ardı etmesini Yoksay düğmesi ile sağlayabilirsiniz. Ayrıca Sözlüğe ekle düğmesiyle bir kelimeyi sözlüğe ekleyebilirsiniz. Yazım denetimi birden çok kullanıcı sözlüğünü destekler, yani kelimenin ekleneceği sözlüğü de seçebilirsiniz.

Ayrıca yazım denetiminin kitaptaki, sadece yanlış olanlar değil, tüm kelimeleri göstermesini sağlayabilirsiniz. Kitabınızdan hangi kelimelere en sık rastlandığını görmek için ve belli kelimeler için arama ve değiştirme çalıştırmak için faydalıdır.

Not

If you make any changes to the book by editing files while the spell check tool is open, you should click the Refresh button in the Spell check tool. If you do not do this and continue to use the Spell check tool, you could lose the changes you have made in the editor.

Not

To exclude an individual file from being spell checked when running the spell check tool, you can use the Exclude files button or add the following comment just under the opening tag in the file:

<!-- calibre-no-spell-check -->

Yeni sözlük ekleme

The spelling checker comes with builtin dictionaries for the English and Spanish languages. You can install your own dictionaries via Preferences → Editor → Manage spelling dictionaries. The spell checker can use dictionaries from the LibreOffice program (in the .oxt format). You can download these dictionaries from The LibreOffice Extensions repository [https://extensions.libreoffice.org/?Tags%5B%5D=50].

Özel karakter eklemek

You can insert characters that are difficult to type by using the Edit → Insert special character tool. This shows you all Unicode characters, simply click on the character you want to type. If you hold Ctrl while clicking, the window will close itself after inserting the selected character. This tool can be used to insert special characters into the main text or into any other area of the user interface, such as the Search and replace tool.

Bir çok karakter olduğundan, ilk önce gösterilmesi için kendi Sık kullanılanlar karakterlerinizi belirtebilirsiniz. Basitçe bir karaktere sağ tıklayıp sık kullanılan olarak işaretleyin. Ayrıca sık kullanılanlardan kaldırmak için sık kullanılanlardaki bir karaktere sağ tıklayabilirsiniz. Son olarak, karakterlerin sırasını Sık kullanılanları yeniden düzenle düğmesine tıklayarak ve karakterleri sürükle bırak yaparak ayarlayabilirsiniz.

You can also directly type in special characters using the keyboard. To do this, you type the Unicode code for the character (in hexadecimal) and then press the Alt+X key which will convert the previously typed code into the corresponding character. For example, to type ÿ you would type ff and then Alt+X. To type a non-breaking space you would use a0 and then Alt+X, to type the horizontal ellipsis you would use 2026 and Alt+X and so on.

Son olarak, HTML isimlendirilmiş birimleri kullanarak da özel karakterleri girebilirsiniz. Örneğin &nbsp; yazmak noktalı virgülü girdiğinizde bir boşlukla değişmesini sağlar. Yer değiştirme yalnızca noktalı virgülü yazarken gerçekleşir.

Kod denetleme görünümü

Bu görünüm ilgilenilen mevcut öğeye uygulanan CSS ve HTML kodunu gösterir. Ön izleme panelinde bir yere sağ tıklayıp Denetle seçerek açabilirsiniz. Bu öğe için HTML kodunu görmenin yanında, daha da önemli olarak CSS biçimlerini de görürsünüz. Dahası, biçimleri düzenleyebilir ve değişikliklerinizin getirdiği etkileri anında görebilirsiniz. Biçimi düzenlemenin kitap içeriğinde değişiklik yapmayacağını not edin, yalnızca hızlıca deneyler yapmaya izin verir. Denetleyici içinde canlı düzenleme yapma işlevi geliştirme altındadır.

Checking external links

You can use this tool to check all links in your book that point to external websites. The tool will try to visit every externally linked website, and if the visit fails, it will report all broken links in a convenient format for you to fix.

Downloading external resources

You can use this tool to automatically download any images/stylesheets/etc. in the book that are not bundled with the book (i.e. they have URLs pointing to a location on the internet). The tool will find all such resources and automatically download them, add them to the book and replace all references to them to use the downloaded files.

Arranging files into folders by type

Often when editing EPUB files that you get from somewhere, you will find that the files inside the EPUB are arranged haphazardly, in different sub-folders. This tool allows you to automatically move all files into sub-folders based on their types. Access it via Tools → Arrange into folders. Note that this tool only changes how the files are arranged inside the EPUB, it does not change how they are displayed in the File browser.

Diğer e-kitap biçimlerindeki dosyaları EPUB olarak içe aktarma

Düzenleyici başka bir e-kitap biçimindeki dosyaları tam bir dönüştürmeye sokmadan EPUB olarak içe aktarmayı destekler. Özellikle kendi elle düzenlenmiş HTML dosyalarınızdan EPUB dosyaları oluşturmak için faydalıdır. Bu işlemi Dosya → HTML veya DOCX dosyasını yeni kitap olarak içe aktar ile yapabilirsiniz.

Raporlar aracı

Düzenleyici kitapta kullanılan dosyalar, resimler, bağlantılar, kelimeler, karakterler ve biçemlerin özetini gösteren güzel bir Raporlar aracı içerir (Araçlar → Raporlar ile). Rapordaki her satır bağlantılıdır. Bir satıra çift tıklamak bu öğenin kitapta kullanıldığı veya tanımlandığı (uygun olduğu üzere) noktaya atlama yapar. Örneğin, Bağlantılar görünümünde Kaynak sütunundaki öğelere çift tıklayarak bağlantının tanımlandığı yere, Hedef sütunundaki girdilere tıklayarak bağlantının gösterdiği yere atlayabilirsiniz.

Kod düzenleyicideki özel özellikler

Calibre HTML Düzenleyici çok güçlüdür. HTML (ve CSS) düzenlemeyi kolaylaştıracak bir çok özelliği vardır.

Sözdizim vurgulama

HTML düzenleyicinin çok gelişmiş sözdizim vurgulama özelliği vardır. Bazı özellikleri:

- Kalın, yatık ve başlık etiketleri içindeki metin kalın/yatık yapılır

- As you move your cursor through the HTML, the matching HTML tags are highlighted, and you can jump to the opening or closing tag with the keyboard shortcuts Ctrl+{ and Ctrl+}. Similarly, you can select the contents of a tag with Ctrl+Alt+T or Ctrl+Shift+T.

- Geçersiz HTML altı kırmızı çizgili olarak vurgulanır

- Spelling errors in the text inside HTML tags and attributes such as title are highlighted. The spell checking is language aware, based on the value of the lang attribute of the current tag and the overall book language.

- <style> etiketleri arasına gömülmüş CSS vurgulanır

- Satır atlatmayan boşluklar, değişik türdeki tireler, vs. gibi görülmesi zor olabilecek özel karakterler vurgulanırlar.

- <a> etiketleri içindeki diğer dosyalara bağlantılar, <img> ve <link> etiketlerindeki dosya isimleri hep vurgulanır. Gösterdikleri dosya ismi mevcut değilse, dosya isminin altı kırmızıyla çizilir.

İçeriğe duyarlı yardım

Bir HTML etiket ismi veya CSS özelliği ismine sağ tıklayarak bu etiket veya özellikle ilgili yardım alabilirsiniz.

You can also hold down the Ctrl key and click on any filename inside a link tag to open that file in the editor automatically. Similarly, Ctrl clicking a class name will take you to the first style rule that matches the tag and class.

Right clicking a class name in an HTML file will allow you to rename the class, changing all occurrences of the class throughout the book and all its stylesheets.

Otomatik tamamlama

When editing an e-book, one of the most tedious tasks is creating links to other files inside the book, or to CSS stylesheets, or images. You have to figure out the correct filename and relative path to the file. The editor has auto-complete to make that easier.

As you type a filename, the editor automatically pops up suggestions. Simply use the Tab key to select the correct file name. The editor even offers suggestions for links pointing to an anchor inside another HTML file. After you type the # character, the editor will show you a list of all anchors in the target file, with a small snippet of text to help you choose the right anchor.

Note that unlike most other completion systems, the editor’s completion system uses subsequence matching. This means that you can type just two or three letters from anywhere in the filename to complete the filename. For example, say you want the filename ../images/arrow1.png, you can simply type ia1 and press Tab to complete the filename. When searching for matches, the completion system prioritizes letters that are at the start of a word, or immediately after a path separator. Once you get used to this system, you will find it saves you a lot of time and effort.

Kesitler

The calibre E-book editor supports snippets. A snippet is a piece of text that is either re-used often or contains a lot of redundant text. The editor allows you to insert a snippet with only a few key strokes. The snippets are very powerful, with many features, such as placeholders you can jump between, automatic mirroring of repeated text and so on. For more information, see Parçacıklar.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- E-kitap düzenleme »

- Function mode for Search & replace in the Editor

Function mode for Search & replace in the Editor

The Search & replace tool in the editor support a function mode. In this mode, you can combine regular expressions (see Calibre’de kurallı ifadelerin kullanımına dair her şey) with arbitrarily powerful Python functions to do all sorts of advanced text processing.

In the standard regexp mode for search and replace, you specify both a regular expression to search for as well as a template that is used to replace all found matches. In function mode, instead of using a fixed template, you specify an arbitrary function, in the Python programming language [https://docs.python.org]. This allows you to do lots of things that are not possible with simple templates.

Fonksiyon kipi ve söz dizimi kullanım teknikleri örneklerle açıklanacak, gittikçe zorlaşan görevleri fonksiyon kipiyle nasıl yapacağınız gösterilecektir.

Belgedeki başlık büyük küçük harflerinin otomatik düzeltilmesi

Burda, düzenleyicideki dahili fonksiyonlardan birini göz önüne alıp başlık etiketleri içindeki tüm metnin harflerini nasıl otomatik olarak başlık boyutuna değiştireceğimize bakacağız:

Find expression: <([Hh][1-6])[^>]*>.+?</\1>

Fonksiyon için, basitçe Başlık-büyük küçük harfli metin (etiketleri yoksay) dahili fonksiyonunu seçin. Bu: <h1>bir BAŞLIK</h1> gibi görünen başlıkları <h1>Bir Başlık</h1> şekline çevirir. Başlık etiketleri içinde başka HTML etiketleri olsa bile çalışır.

İlk özel fonksiyonunuz - akıllı tireler

Fonksiyon kipinin asıl gücü metni keyfi şekillerde işlemenizi sağlayan kendi fonksiyonlarınızı yazabilmenizden gelir. Düzenleyicideki Akıllı Noktalama aracı bağımsız tirelere dokunmaz, yani bu fonksiyonu kullanarak onları uzun tirelerle değiştirebilirsiniz.

To create a new function, simply click the Create/edit button to create a new function and copy the Python code from below.

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
return match.group().replace('--', '—').replace('-', '—')

Every Search & replace custom function must have a unique name and consist of a Python function named replace, that accepts all the arguments shown above. For the moment, we won’t worry about all the different arguments to replace() function. Just focus on the match argument. It represents a match when running a search and replace. Its full documentation in available here [https://docs.python.org/library/re.html#match-objects]. match.group() simply returns all the matched text and all we do is replace hyphens in that text with em-dashes, first replacing double hyphens and then single hyphens.

Bu fonksiyonu arama düzenli ifadesi ile kullanın:

>[^<>]+<

Tüm tireleri uzun tire ile değiştirecektir, ama yalnızca asıl metin içinde HTML etiket tanımlarında değil.

Fonksiyon kipinin gücü - yanlış tirelenmiş kelimeler için bir yazım denetim sözlüğü kullanmak

Often, e-books created from scans of printed books contain mis-hyphenated words – words that were split at the end of the line on the printed page. We will write a simple function to automatically find and fix such words.

import regex
from calibre import replace_entities
from calibre import prepare_string_for_xml

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):

def replace_word(wmatch):
# Try to remove the hyphen and replace the words if the resulting
# hyphen free word is recognized by the dictionary
without_hyphen = wmatch.group(1) + wmatch.group(2)
if dictionaries.recognized(without_hyphen):
return without_hyphen
return wmatch.group()

# Search for words split by a hyphen
text = replace_entities(match.group()[1:-1]) # Handle HTML entities like &amp;
corrected = regex.sub(r'(\w+)\s*-\s*(\w+)', replace_word, text, flags=regex.VERSION1 | regex.UNICODE)
return '>%s<' % prepare_string_for_xml(corrected) # Put back required entities

Bu fonksiyonu aynı bulma ifadesi ile kullanın, açmak gerekirse:

>[^<>]+<

Böylece kitap metnindeki tüm yanlış tirelenmiş kelimeleri sihirli bir şekilde düzeltecektir. Ana nokta replace fonksiyonuna verilen kullanışlı bir ek bağımsız değişken olan dictionaries. Bu düzenleyicinin kitap metninde yazım denetimi yaptığı sözlükleri kasteder. Bu fonksiyonun yaptığı ise bir tire ile ayrılmış kelimelere bakmak, tireyi kaldırmak ve karma kelimenin sözlük tarafından tanınıp tanınmadığına bakmaktır, eğer tanınıyorsa asıl kelimeler tire olmayan karma kelimelerle değiştirilir.

Bu tekniğin kısıtlarından biri yalnızca tek dilli kitaplarda çalışacak olmasıdır, çünkü var sayılan olarak, dictionaries.recognized() kitabın ana dilini kullanır.

Kısımların otomatik numaralandırılması

Şimdi biraz daha farklı bir şey göreceğiz. HTML dosyanızın birçok kısmı olduğunu farz edin, her biri bir <h2> etiketi içinde <h2>Bir metin</h2> gibi görünen başlıkla. Özel bir fonksiyon oluşturup bu başlıkları otomatik olarak sıralı numaralar şeklinde numaralandırabilirsiniz, böylece <h2>1. Bir metin</h2> olarak görünürler.

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
section_number = '%d. ' % number
return match.group(1) + section_number + match.group(2)

# Ensure that when running over multiple files, the files are processed
# in the order in which they appear in the book
replace.file_order = 'spine'

Arama ifadesiyle birlikte kullanın:

(?s)(<h2[^<>]*>)(.+?</h2>)

İmleci dosyanın üstüne konumlandırın ve Hepsini değiştir tıklayın.

Bu fonksiyon replace() fonksiyonuna ek başka kullanışlı bir bağımsız değişken kullanır: number değişkeni. Hepsini değiştir yaparken sayı otomatik olarak her başarılı eşleşmede artırılır.

Yeni başka bir özellik replace.file_order kullanımıdır – bunun 'spine' olarak ayarlanması bu aramanın birden fazla HTML dosyada aranması durumunda, dosyaların kitapta bulundukları sırada işlenmesini anlamına gelir. Detaylar için bknz Çoklu HTML dosyalarında çalışırkenki dosya sırasını seç.

Otomatik olarak bir İçindekiler oluştur

Son olarak, biraz daha iddialı bir şey deneyelim. Kitabınızda <h1 id="birtakimid">Bir Metin</h1> gibi görünen h1 ve h2 etiketli başlıklar olduğunu var sayın. Bu başlıklara dayalı bir İçindekileri otomatik olarak üreteceğiz. Aşağıdaki özel fonksiyonu oluşturun:

from calibre import replace_entities from calibre.ebooks.oeb.polish.toc import TOC, toc_to_html from calibre.gui2.tweak_book import current_container from calibre.ebooks.oeb.base import xml2str def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs): if match is None: # All matches found, output the resulting Table of Contents. # The argument metadata is the metadata of the book being edited if 'toc' in data: toc = data['toc'] root = TOC() for (file_name, tag_name, anchor, text) in toc: parent = root.children[-1] if tag_name == 'h2' and root.children else root parent.add(text, file_name, anchor) toc = toc_to_html(root, current_container(), 'toc.html', 'Table of Contents for ' + metadata.title, metadata.language) print(xml2str(toc)) else: print('No headings to build ToC from found') else: # Add an entry corresponding to this match to the Table of Contents if 'toc' not in data: # The entries are stored in the data object, which will persist # for all invocations of this function during a 'Replace All' operation data['toc'] = [] tag_name, anchor, text = match.group(1), replace_entities(match.group(2)), replace_entities(match.group(3)) data['toc'].append((file_name, tag_name, anchor, text)) return match.group() # We don't want to make any actual changes, so return the original matched text

# Ensure that we are called once after the last match is found so we can # output the ToC replace.call_after_last_match = True # Ensure that when running over multiple files, this function is called, # the files are processed in the order in which they appear in the book replace.file_order = 'spine'

Ve bulma ifadesiyle kullanın:

<(h[12]) [^<>]* id=['"]([^'"]+)['"][^<>]*>([^<>]+)

Run the search on All text files and at the end of the search, a window will popup with “Debug output from your function” which will have the HTML Table of Contents, ready to be pasted into toc.html.

The function above is heavily commented, so it should be easy to follow. The key new feature is the use of another useful extra argument to the replace() function, the data object. The data object is a Python dictionary that persists between all successive invocations of replace() during a single Replace All operation.

Başka yeni bir özellik de call_after_last_match kullanımıdır – bunu replace() fonksiyonunda True olarak ayarlamak düzenleyicinin tüm eşleşmeler bulunduktan sonra bir kere daha replace() çağırması demektir. Bu ek çağrı için, eşleşme nesnesi None olacaktır.

Bu yalnızca fonksiyon kipinin gücünü göstermek için bir gösteriydi, gerçekten bir kitaptaki başlıklardan İçindekiler oluşturmaya ihtiyacınız varsa, Araçlar → İçindekiler içindeki bu iş için atanmış İçindekiler aracını kullansanız daha iyi edersiniz.

Fonksiyon kipi için API

All function mode functions must be Python functions named replace, with the following signature:

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
return a_string

Bir bul/değiştir çalıştığında, bulunan her eşleşme için, bu eşleşme için değiştirme karater dizisini döndürmesi gereken replace() fonksiyonu çağrılacaktır. Herhangi bir değiştirme yapılmayacaksa, asıl karakter dizisi olan match.group() döndürmelidir. replace() fonksiyonuna verilen çeşitli bağımsız değişkenler aşağıda belgelenmiştir.

match bağımsız değişkeni

The match argument represents the currently found match. It is a Python Match object [https://docs.python.org/library/re.html#match-objects]. Its most useful method is group() which can be used to get the matched text corresponding to individual capture groups in the search regular expression.

number bağımsız değişkeni
number bağımsız değişkeni mevcut eşleşmenin sayısıdır. Hepsini Değiştir çalıştırdığınızda, her başarılı eşleşme ``replace()``in artan bir sayıyla çağrılmasına sebep olur. İlk eşleşmenin 1 numarası vardır.

file_name bağımsız değişkeni

Mevcut eşleşmenin bulunduğu dosyanın dosya ismidir. İşaretli metin içinde arama yaparken, file_name boştur. file_name kuralsal biçimindedir, ayraç olarak / kullanan kitabın köküne göreceli bir yol.

metadata bağımsız değişkeni

Bu mevcut kitabın metadata’sını temsil eder, başlık, yazarlar, dil, vs. Bir calibre.ebooks.metadata.book.base.Metadata nesnesidir. Kullanışlı öznitelikler arasında başlık, yazarlar (yazar listesi) ve dil (dil kodu) bulunur.

dictionaries bağımsız değişkeni

This represents the collection of dictionaries used for spell checking the current book. Its most useful method is dictionaries.recognized(word) which will return True if the passed in word is recognized by the dictionary for the current book’s language.

data bağımsız değişkeni

This a simple Python dictionary. When you run Replace all, every successive match will cause replace() to be called with the same dictionary as data. You can thus use it to store arbitrary data between invocations of replace() during a Replace all operation.

functions bağımsız değişkeni
functions bağımsız değişkeni diğer tüm kullanıcı tanımlı fonksiyonlara erişim verir. Bu kod tekrar kullanımı için faydalıdır. Yardımcı fonksiyonları bir yerde tanımlayabilir ve diğer tüm fonksiyonlarınızda tekrar kullanabilirsiniz. Örneğin, şu şekilde My Function isimli bir fonksiyon tanımladığınızı var sayalım:

def utility():
# do something

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
...

Sonra, başka bir fonksiyon içinde, utility() fonksiyonuna şu şekilde erişebilirsiniz:

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
utility = functions['My Function']['utility']
...

Fonksiyonlar nesnesini diğer fonksiyonlarca tekrar kullanılabilen kalıcı veri depolamak için de kullanabilirsiniz. Örneğin, Hepsini Değiştir ile çağrıldığında veri toplayan bir fonksiyonunuz ve daha sonra çağrıldığında bunu kullanan ikinci bir fonksiyonunuz olabilir. Şu iki fonksiyonu göz önüne alın:

# Function One
persistent_data = {}

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
...
persistent_data['something'] = 'some data'

# Function Two
def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
persistent_data = functions['Function One']['persistent_data']
...

Fonksiyonlarınızda hata ayıklama

You can debug the functions you create by using the standard print() function from Python. The output of print will be displayed in a popup window after the Find/replace has completed. You saw an example of using print() to output an entire table of contents above.

Çoklu HTML dosyalarında çalışırkenki dosya sırasını seç

When you run a Replace all on multiple HTML files, the order in which the files are processes depends on what files you have open for editing. You can force the search to process files in the order in which the appear by setting the file_order attribute on your function, like this:

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
...

replace.file_order = 'spine'

file_order iki değer alır, aramanın birden çok dosyayı kitapta göründükleri sırada işlemesine sebep olan ve sırayla ileri veya geri anlamına gelen``spine`` ve spine-reverse.

Fonksiyonunuzun son eşleşme bulunduktan sonra ek bir sefer daha çağrılması

Bazen, yukarıdaki içindekileri otomatik olarak üreten örnek gibi, son eşleşme bulunduktan sonra fonksiyonunuzu bir kere daha çağırmak faydalı olabilir. Bunu fonksiyonunuzda call_after_last_match özniteliğini şu şekilde ayarlayarak yapabilirsiniz:

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
...

replace.call_after_last_match = True

Appending the output from the function to marked text

When running search and replace on marked text, it is sometimes useful to append so text to the end of the marked text. You can do that by setting the append_final_output_to_marked attribute on your function (note that you also need to set call_after_last_match), like this:

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
...
return 'some text to append'

replace.call_after_last_match = True
replace.append_final_output_to_marked = True

Suppressing the result dialog when performing searches on marked text

You can also suppress the result dialog (which can slow down the repeated application of a search/replace on many blocks of text) by setting the suppress_result_dialog attribute on your function, like this:

def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
...

replace.suppress_result_dialog = True

More examples

More useful examples, contributed by calibre users, can be found in the calibre E-book editor forum [https://www.mobileread.com/forums/showthread.php?t=237181].

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- E-kitap düzenleme »
- Parçacıklar

Parçacıklar

Calibre E-kitap düzenleyici parçacıkları destekler. Parçacık, sıklıkla yeniden kullanılan veya çok fazla gereksiz metin içeren bir metin parçasıdır. Düzenleyici, yalnızca birkaç tuş vuruşuyla bir pasaj eklemenize olanak tanır. Örneğin, HTML dosyalarını düzenlerken kendinizi sık sık bağlantı etiketleri eklerken bulduğunuzu varsayalım, o zaman düzenleyiciye <a yazıp Control+J tuşuna basabilirsiniz. Editör bunu şu şekilde genişletecektir:

<a href="filename"></a>

Yalnızca bu değil, filename kelimesi imleç üzerinde olacak şekilde seçilecektir, böylece gerçek dosya ismini, düzenleyicinin Otomatik tamamlama özelliği ile girebilirsiniz. Dosya adını yazmayı bitirdiğinizde ve Control+J ye tekrar bastığınızda imleç <a> etiketleri arasına sıçrar böylece bağlantı için metni de kolaylıkla yazabilirsiniz.

Düzenleyicideki kesit sistemi oldukça gelişmiştir, birkaç dahili kesit bulunmaktadır ve düzenleme biçeminize göre kendi kesitlerinizi de oluşturabilirsiniz.

Dahili kesitlerle ilgili şu tartışma kesit sisteminin gücünü gösteriyor olmalı.

Not

Ayrıca Ara ve değiştir panelindeki metin giriş alanlarında da parçacıklar kullanabilirsiniz, ancak yer tutucular (atlamak için Control+J kullanarak) çalışmayacaktır.

Dahili kesitler

Dahili kesitler aşağıda tarif edilmiştir. Aynı tetikleme metni ile oluşturduğunuz kendi kesitlerinizle bunların üzerine yazabileceğinizi unutmayın.

Doldurma metni ekleme [Lorem]

İlk yerleşik kod parçası ve en basiti, bir belgeye dolgu metni eklemek için kullanılır. Dolgu metni, Cicero’nun felsefi bir çalışmasından (İngilizceye çevrilmiş) ‘De finibus bonorum et malorum <https://en.wikipedia.org/wiki/De_finibus_bonorum_et_malorum>’_ alınmıştır. Bunu kullanmak için bir HTML dosyasına Lorem yazın ve Control+J tuşuna basın. Bunun yerine birkaç paragraflık dolgu maddesi konulacaktır.

Bu kesitin tanımı oldukça basit, tetikleyici metin Lorem olarak tanımlıdır ve şablon basitçe eklenecek gerçek metindir. Kolaylıkla özelleştirerek kendi doldurma metninizi girecek biçime getirebilirsiniz.

Kendisi kapanan bir HTML etiketi [<>] eklemek

Şimdi *yer tutucular*la ilgili güçlü ve basit bir örneğe bakalım. Kendisi kapanan <hr/> etiketi girmek istediğinizi varsayalım. <> yazın ve Control+J’ye basın, düzenleyici kesidi şu şekilde genişletir:

<|/>

Burda, | sembolü mevcut imleç konumunu temsil eder. Ardından hr yazıp Control+J basarak imleci etiketin sonuna taşıyabilirsiniz. Bu kesit şöyle tanımlanmıştır:

Trigger: <>
Template: <$1/>$2

Yer tutucular basitçe dolar ($) işareti ve ardından bir numara şeklindedir. Control+J tuşu ile kesit genişletildiğinde imleç ilk yer tutucuya gider (en düşük numaralı yer tutucu). Control+J ye tekrar bastığınızda imleç sonraki yer tutucuya gider (sonraki en büyük numaralı yer tutucu).

Bir HTML bağlantı etiketi eklemek [<a]

HTML bağlantı etiketlerinin hepsi yaygın bir yapı kullanır. Bir href öznitelikleri ve açma ve kapama etiketleri arasında bir miktar metin bulunur. Bunları yazmayı daha etkin hale getirecek bir kesit bizi yer tutucuların başka bir takım özellikleriyle tanıştıracaktır. Bu kesiti kullanmak için, basitçe <a yazın ve Control+J basın. Düzenleyici şu şekilde genişletir:

<a href="filename|"></a>

Yalnızca bu değil, filename kelimesi imleç üzerinde olacak şekilde seçilir, böylece düzenleyicinin Otomatik tamamlama özelliği ile basitçe gerçek dosya ismini girebilirsiniz. Ve bir kere dosya adını yazmayı bitirdiğinizde, tekrar Control+J ye basarak imleci <a> etiketleri arasına getirebilir, bağlantı için metni kolayca yazabilirsiniz. Metni yazmayı bitirdiğinizde, tekrar Control+J basarak kapatma etiketi sonrasındaki konuma gidebilirsiniz. Bu kesit şöyle tanımlanır:

Trigger: <a
Template: <a href="${1:filename}">${2*}</a>$3

Burada birkaç yeni özellik var. Öncelikle $1 yer tutucusu daha karmaşık hale geldi. Artık bazı varsayılan metinleri (dosya adı sözcüğü) içeriyor. Yer tutucu varsayılan metin içeriyorsa, parçacık genişletildiğinde yer tutucunun yerine varsayılan metin kullanılır. Ayrıca Control+J kullanarak varsayılan metin içeren bir yer tutucuya atladığınızda varsayılan metin seçilir. Bu şekilde, şablonun önemli kısımlarını doldurmanız konusunda size hatırlatma görevi görecek varsayılan metni kullanabilirsiniz. Bir yer tutucu için varsayılan metni şu sözdizimini kullanarak belirtebilirsiniz: ${<number>:varsayılan metin}.

İkinci yeni özellik de ikinci yer tutucunun sonunda bir asterisk olmasıdır (${2*}). Bunun anlamı şablonu genişletmeden önce seçilen herhangi bir metnin yer tutucuyla değiştirilecek olmasıdır. Bunu gerçekte görmek için, düzenleyicide biraz metin seçin, Control+J basın, <a yazın ve |ct|ye tekrar basın, şablon şu şekilde genişletilir:

<a href="filename">whatever text you selected</a>

Bir HTML resim etiketi eklemek [<i]

Yukarıda gösterilen HTML bağlantı eklemeye çok benzerdir. Hızlıca bir <img src="dosyadı" alt="tanımlama" /> etiketi eklemenize ve src and alt öznitelikleri arasında gezinmenize izin verir:

Trigger: <i
Template: <img src="${1:filename}" alt="${2*:description}" />$3

Gelişigüzel bir HTML etiketi ekle [<<]

Bu, isteğe bağlı bir tam HTML etiketi eklemenize (veya önceden seçilen metni etikete sarmanıza) olanak tanır. Kullanmak için << yazıp Control+J tuşuna basmanız yeterlidir. Editör bunu şu şekilde genişletecektir:

<|></>

Etiket ismini girin, örneğin: span ve Control+J ye basın, sonuç şöyle olur:

<span>|</span>

Kapanış etiketinin otomatik olarak span ile doldurulduğunu fark etmiş olmalısınız. Bu da, yer tutucuların başka bir özelliği ile, aynalama ile elde edilir. Aynalama basitçe, aynı yer tutucuyu bir şablonda birden fazla kere belirtirseniz, ikinci ve sonraki tüm konumların Control+J ye bastığınızda otomatik olarak ilk konumda yazdığınızla doldurulacak olmasıdır. Bu kesitin tanımı şu şekilde:

Trigger: <<
Template: <$1>${2*}</$1>$3

Gördüğünüz gibi, ilk yer tutucu ($1) iki kere belirtilmiş, ikinci seferde kapanış etiketinde, açılış etiketinde ne yazdıysanız basitçe kopyalanacaktır.

Bir sınıf özniteliği ile [<c] gelişi güzel bir HTML etiketi eklemek

Bu yukarıdaki gelişi güzel etiket ekleme örneğiyle çok benzerdir, yalnızca etiket için bir sınıf belirtmek istediğinizi var sayar:

Trigger: <c
Template: <$1 class="${2:classname}">${3*}</$1>$4

Bu önce etiket ismini yazmanızı, ardından Control+J basmanızı, sınıf ismini yazmanızı, Control+J basıp etiket içeriğini doldurmanızı ve son defa Control+J basarak etiket dışına zıplamanızı sağlar. Kapatma etiketi otomatik olarak doldurulur.

Kendi kesitlerinizi oluşturmak

Kesitler gerçekten öne çıkıyor, çünkü düzenleme biçeminize göre kendi kesitlerinizi oluşturabiliyorsunuz. Kendi kesitlerinizi oluşturmak için düzenleyicide Düzenle → Tercihler → Düzenleyici ayarları → Kesitleri yönet menüsüne gidin. Bu, kendi kesitlerinizi oluşturmanızı sağlayacak kullanımı kolay bir iletişim penceresi açar. Basitçe Kesit ekle ye tıklayın ve şöyle bir iletişim penceresi görün:

Önce kesitinize bir isim verin, açıklayıcı bir şey olsun, böylece ilerde ne olduğunu hatırlarsınız. Ardından bir tetikleyici belirtin. Tetikleyici basitçe Control+J ye basıp kesidi genişletmek için düzenleyicide yazmanız gereken metindir.

Ardından snippet şablonunu belirtin. Yukarıdaki örneklerden biriyle başlamalı ve onu ihtiyaçlarınıza uyacak şekilde değiştirmelisiniz. Son olarak snippet’in hangi dosya türleri için etkin olmasını istediğinizi belirtin. Bu şekilde, farklı dosya türlerinde farklı şekilde çalışan, aynı tetikleyici metne sahip birden fazla parçacığa sahip olabilirsiniz.

Sonraki adım yeni oluşturduğunuz kesidi test etmektir. Aşağıdaki Test kutusunu kullanın. Tetikleyici metni girin ve Control+J ye basarak kesidi genişletin ve yer tutucular arasında zıplayın.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Calibre İçerik sunucusu

Calibre İçerik sunucusu
calibre İçerik sunucusu calibre kitaplıklarınıza erişmenizi ve kitapları doğrudan favori cep telefonunuz veya tablet bilgisayarınızdaki bir tarayıcıdan okumanızı sağlar. Sonuç olarak, telefonunuza herhangi bir özel kitap okuma/yönetim uygulaması yüklemenize gerek yoktur. Sadece tarayıcıyı kullanın. Sunucu, okuduğunuz kitabı çevrimdışı bir önbellekte indirir ve saklar, böylece internet bağlantısı olmadığında bile okuyabilirsiniz.

İçindekiler

- İçerik sunucusuna diğer cihazlardan erişme

- Sunucuya ev ağınızdaki cihazlardan erişme

- İnternetin olduğu her yerden sunucuya erişim

- Sunucu arayüzü

- Kitap listesi

- Kitap okuyucusu

- Tarayıcı desteği

- Çevrimdışı desteği etkinleştirme

- Managing user accounts from the command-line only

- Integrating the calibre Content server into other servers

- Using a full virtual host

- Using a URL prefix

- Creating a service for the calibre server on a modern Linux system

Sunucuyu başlatmak için Bağlan/paylaş düğmesine tıklayın ve İçerik sunucusunu başlat`ı seçin. Bilgisayarınızın güvenlik duvarından veya anti-virüs programından “calibre.exe”ye erişime izin vermenin uygun olup olmadığını soran bir mesaj alabilirsiniz. ``İzin Ver` veya Tamam düğmesini tıklayın. Daha sonra bilgisayarınızda bir tarayıcı (tercihen Chrome veya Firefox) açın ve aşağıdaki adresi yazın:

http://127.0.0.1:8080

This will open a page in the browser showing you your calibre libraries, click on any one and browse the books in it. Click on a book, and it will show you all the metadata about the book, along with buttons to Read and Download the book. Click the Read button to start reading the book.

Not

Yukarıda kullanılan adres http://127.0.0.1:8080 yalnızca calibre’yi çalıştıran bilgisayarda çalışacaktır. Sunucuya diğer bilgisayarlardan/telefonlardan/tabletlerden vb. erişmek için. Bir sonraki bölümde açıklandığı gibi biraz daha çalışmanız gerekecek.

İçerik sunucusuna diğer cihazlardan erişme

Genellikle ihtiyaç duyacağınız iki tür uzaktan cihaz erişimi vardır. İlk, daha basit tür, ev ağınızın içindendir. Calibre’yi ev ağınızdaki bir bilgisayarda çalıştırıyorsanız ve diğer cihazlarınızı da aynı ev ağına bağladıysanız, bu cihazlardaki sunucuya kolayca erişebilmeniz gerekir.

Sunucuya ev ağınızdaki cihazlardan erişme

Sunucuyu yukarıda açıklandığı gibi calibre’de başlattıktan sonra Bağlan/paylaş düğmesine tekrar tıklayın. İçerik sunucusunu Başlat eylemi yerine, İçerik sunucusunu Durdur eylemini görmelisiniz. Bu eylemin sağında bir IP adresi ve port numarası listelenecektir. Bunlar noktalarla ayrılmış bir grup sayıya benziyor. Örneğin:

Stop Content server [192.168.1.5, port 8080]

Bu numaralar, cihazlarınızdaki sunucuya bağlanmak için hangi adresi kullanmanız gerektiğini size söyler. Yukarıdaki örneği takip edersek adres şu şekilde olur:

http://192.168.1.5:8080

Adresin ilk kısmı her zaman http:// olur, sonraki kısım IP adresidir, yani virgülden önceki sayılardır ve son olarak IP adresine iki nokta üst üste () eklenmesi gereken port numarasını alırız. :). Şanslıysanız ihtiyacınız olan tek şey bu olmalı ve cihazınızdaki calibre kitaplıklarına bakıyor olacaksınız. Değilse, okumaya devam edin.

Ev ağı bağlantısında sorun giderme

Cihazınızdan sunucuya erişemiyorsanız aşağıdaki adımları deneyin:

1. Sunucuyla aynı bilgisayarda çalışan bir tarayıcıda http://127.0.0.1:8080 adresini açarak sunucunun çalışıp çalışmadığını kontrol edin.

2. Güvenlik duvarınızın/anti-virüsünüzün bilgisayarınıza 8080 bağlantı noktasından ve calibre programına bağlantılara izin verdiğini kontrol edin. Sorunların kaynağı olan güvenlik duvarını/antivirüsü ortadan kaldırmanın en kolay yolu, her ikisini de geçici olarak kapatmak ve ardından bağlanmayı denemektir. Bilgisayarınızı güvende tutmak için güvenlik duvarını kapatmadan önce öncelikle internet bağlantısını kesmelisiniz.

3. Cihazınızın ve bilgisayarınızın aynı ağda olup olmadığını kontrol edin. Bu, her ikisinin de aynı kablosuz yönlendiriciye bağlı olması gerektiği anlamına gelir. Özellikle hücresel veya İSS tarafından sağlanan doğrudan WiFi bağlantısı kullanılmamalıdır.

4. Standart dışı ağ kurulumunuz varsa, Bağlan/paylaş menüsünde gösterilen IP adresi yanlış olabilir. Böyle bir durumda kullanacağınız doğru IP adresinin ne olduğunu kendiniz bulmanız gerekecektir. Ne yazık ki, mümkün olan ağ yapılandırmalarının sonsuz çeşitliliği göz önüne alındığında, bunu yapmak için size bir yol haritası vermek mümkün değildir.

5. Bir kullanıcı adı ve şifre ayarladıysanız, önce sorun yaratıp yaratmadığını görmek için bunları kullanmadan deneyin. Bazı e-mürekkep cihazlarında kimlik doğrulama işlemini gerçekleştiremeyen tarayıcılar bulunur. Bazen kullanıcı adını ve şifreyi URL’ye ekleyerek bu sorunu çözebilirsiniz, örneğin: http://kullanıcıadı:şifre@192.168.1.2:8080.

6. Eğer takılırsanız, her zaman calibre kullanıcı forumlarından [https://www.mobileread.com/forums/forumdisplay.php?f=166] yardım isteyebilirsiniz.

İnternetin olduğu her yerden sunucuya erişim

Uyarı

Bunu yapmadan önce sunucuda kullanıcı adı/şifre korumasını açmalısınız, aksi takdirde dünyadaki herkes kitaplarınıza erişebilir. Tercihler → Paylaşım → Ağ üzerinden paylaşım seçeneğine gidin ve İçerik sunucusuna erişmek için kullanıcı adı ve şifre iste seçeneğini etkinleştirin.

İnternet erişimini ayarlamaya ilişkin belirli ayrıntılar, ağ yapılandırmasına ve kullandığınız bilgisayar türüne göre değişiklik gösterse de temel şema aşağıdaki gibidir.

1. Sunucuyu çalıştıracağınız bilgisayarın harici IP adresini öğrenin. Bunu, bilgisayarda çalışan bir tarayıcıda IP adresim nedir [https://www.whatismyip.com/] sitesini ziyaret ederek yapabilirsiniz.

2. Bilgisayar bir yönlendiricinin arkasındaysa, 8080 bağlantı noktasını (veya calibre İçerik sunucusunu çalıştırmak için seçtiğiniz herhangi bir bağlantı noktasını) bilgisayara iletmek için yönlendiricide bağlantı noktası iletmeyi etkinleştirin.

3. Calibre sunucusuna bilgisayarınızdaki herhangi bir güvenlik duvarı/anti-virüs programı üzerinden izin verildiğinden emin olun.

4. Artık, ilk adımda bulduğunuz IP adresini kullanarak internete bağlı herhangi bir cihazdaki sunucuya erişebilmelisiniz. Örneğin, bulduğunuz IP adresi “123.123.123.123” ise ve calibre sunucusu için kullandığınız bağlantı noktası “8080” ise, cihazınızda kullanılacak adres şu şekilde olur: “http://123.123” .123.123:8080``.

5. İsteğe bağlı olarak, ilk adımda bulduğunuz IP adresi yerine hatırlanması kolay bir adres ayarlamak için “no-ip <https://www.noip.com/free>”_ gibi bir hizmet kullanın.

Not

Maksimum güvenlik için İçerik sunucusunda HTTPS’yi de etkinleştirmelisiniz. Sunucunun gelişmiş yapılandırma seçeneklerinde kullanılacak HTTPS sertifikasının yolunu sağlayarak bunu doğrudan sunucuda yapabilirsiniz veya mevcut bir HTTPS kurulumunu kullanmak için aşağıda açıklandığı gibi bir ters proxy ayarlayabilirsiniz.

Sunucu arayüzü

Sunucu arayüzü ana calibre arayüzünün basitleştirilmiş bir versiyonudur, dokunmatik ekranlarla kullanım için optimize edilmiştir. Ana ekran size şu anda okuduğunuz kitapları gösterir ve ayrıca göz atmak istediğiniz calibre kitaplığını seçmenize olanak tanır. Calibre’deki sunucu, daha önce olduğu gibi yalnızca tek bir kütüphaneye değil, tüm kütüphanelerinize erişmenizi sağlar.

Kitap listesi

The server book list is a simple grid of covers. Tap on a cover to see the detailed metadata for a book, or to read the book. If you prefer a more detailed list, you can change the default view by clicking the three vertical dots in the top right corner.

Sorting and searching of the book list should be familiar to calibre users. They can be accessed by clicking their icons in the top right area. They both work exactly the same as in the main calibre program. The search page even allows you to construct search queries by clicking on authors/tags/etc., just as you can using the Tag browser in the main program.

A much loved feature of the main program, Virtual libraries is present in the server interface as well. Click the three vertical dots in the top right corner to choose a Virtual library.

Kitap okuyucusu

You can read any book in your calibre library by simply tapping on it and then tapping the Read button. The book reader is very simple to operate. You can both tap and swipe to turn pages. Swiping up/down skips between chapters. Tapping the top quarter of the screen gets you the detailed controls and viewer preferences.

İçerik sunucusunu çalışır durumda bırakırsanız, aynı kitabı birden fazla cihazda bile açabilirsiniz; bu, son okuma konumunuzu hatırlayacaktır. Aksi takdirde, üst çeyreğe dokunup Sync seçeneğini seçerek senkronizasyonu zorlayabilirsiniz.

Tarayıcı desteği

Yeni calibre sunucusu gelişmiş HTML 5 ve CSS 3 özelliklerinin çoğunu kullanıyor. Bu nedenle kullanmak için güncel bir tarayıcı gerektirir. Masaüstünde Android Chrome ve iOS Safari’nin yanı sıra Chrome ve Firefox’ta da test edilmiştir.

Sunucu, halihazırda standartlaştırılmış veya standartlar yolunda olan işlevleri kullanmaya özen göstermektedir. Bu nedenle, şu anda favori tarayıcınızla çalışmıyorsa, muhtemelen o tarayıcı yetiştiğinde çalışacaktır.

Özellikle eski veya sınırlı bir tarayıcı kullanıyorsanız veya JavaScript çalıştırmayı sevmiyorsanız, sunucu adresine /mobile ekleyerek mobil görünümünü kullanabilirsiniz.

Not
iOS’ta Apple yalnızca tek bir tarayıcı motoruna izin verir, dolayısıyla Firefox, Chrome ve Safari’nin hepsi aslında aynı tarayıcıdır. Yeni sunucu arayüzü iOS 10.3.2 veya daha yenisini gerektirir. Android’de sunucu, Chrome sürüm 58 ve daha yenisiyle test edilmiştir.

Çevrimdışı desteği etkinleştirme

Browser makers have been trying to force people to use SSL by disabling advanced features in their browsers for plain HTTP connections. One such casualty is ApplicationCache, which was what was used in calibre for offline support. As a result now-a-days sadly, offline mode works only as long as you keep the browser tab open. In addition, in Firefox on Android, you will need to type about:config and create a preference called browser.tabs.useCache and set it to true.

Managing user accounts from the command-line only

The calibre program has a nice section in Preferences to allow you to manage user accounts for the server. However, if you want to run the standalone server and cannot run the main calibre program on the same computer/user account, you can also manage users using just the command-line.

You can manage user accounts using the --manage-users option to the standalone calibre-server program. Suppose you want to store the user database in the folder /srv/calibre, then you create it by running:

calibre-server --userdb /srv/calibre/users.sqlite --manage-users

Just follow the prompts to create user accounts, set their permission, etc. Once you are done, you can run the server as:

calibre-server --userdb /srv/calibre/users.sqlite --enable-auth

It will use the user accounts you created in the previous step.

Integrating the calibre Content server into other servers

Here, we will show you how to integrate the calibre Content server into another server. The most common reason for this is to make use of SSL or to serve the calibre library as part of a larger site. The basic technique is to run the calibre server and setup a reverse proxy to it from the main server.

A reverse proxy is when your normal server accepts incoming requests and passes them onto the calibre server. It then reads the response from the calibre server and forwards it to the client. This means that you can simply run the calibre server as normal without trying to integrate it closely with your main server.

Using a full virtual host

The simplest configuration is to dedicate a full virtual host to the calibre server. In this case, run the calibre server as:

calibre-server

Now setup the virtual host in your main server, for example, for nginx:

http {
client_max_body_size 64M; # needed to upload large books
}

server {
listen [::]:80;
server_name myserver.example.com;

location / {
proxy_pass http://127.0.0.1:8080;
}
}

Or, for Apache:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

<VirtualHost *:80>
ServerName myserver.example.com
AllowEncodedSlashes On
ProxyPreserveHost On
ProxyPass "/" "http://localhost:8080/"
</VirtualHost>

Using a URL prefix

If you do not want to dedicate a full virtual host to calibre, you can have it use a URL prefix. Start the calibre server as:

calibre-server --url-prefix /calibre --port 8080

The key parameter here is --url-prefix /calibre. This causes the Content server to serve all URLs prefixed by /calibre. To see this in action, visit http://localhost:8080/calibre in your browser. You should see the normal Content server website, but now it will run under /calibre.

With nginx, the required configuration is:

http {
client_max_body_size 64M; # needed to upload large books
}

proxy_set_header X-Forwarded-For $remote_addr;
location /calibre/ {
proxy_buffering off;
proxy_pass http://127.0.0.1:8080$request_uri;
}
location /calibre {
# we need a trailing slash for the Application Cache to work
rewrite /calibre /calibre/ permanent;
}

For Apache, first enable the proxy modules in Apache, by adding the following to httpd.conf:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

The exact technique for enabling the proxy modules will vary depending on your Apache installation. Once you have the proxy modules enabled, add the following rules to httpd.conf (or if you are using virtual hosts to the conf file for the virtual host in question):

AllowEncodedSlashes On
RewriteEngine on
RewriteRule ^/calibre/(.*) http://127.0.0.1:8080/calibre/$1 [proxy]
RedirectMatch permanent ^/calibre$ /calibre/

That’s all, you will now be able to access the calibre Content server under the /calibre URL in your main server. The above rules pass all requests under /calibre to the calibre server running on port 8080 and thanks to the --url-prefix option above, the calibre server handles them transparently.

Not

When using a reverse proxy, you should tell the calibre Content server to only listen on localhost, by using --listen-on 127.0.0.1. That way, the server will only listen for connections coming from the same computer, i.e. from the reverse proxy.

Not

If you have setup SSL for your main server, you should tell the calibre server to use basic authentication instead of digest authentication, as it is faster. To do so, pass the --auth-mode=basic option to calibre-server.

Creating a service for the calibre server on a modern Linux system

You can easily create a service to run calibre at boot on a modern (systemd [https://www.freedesktop.org/wiki/Software/systemd/]) based Linux system. Just create the file /etc/systemd/system/calibre-server.service with the contents shown below:

[Unit]
Description=calibre Content server
After=network.target

[Service]
Type=simple
User=mylinuxuser
Group=mylinuxgroup
ExecStart=/opt/calibre/calibre-server "/path/to/calibre library folder"

[Install]
WantedBy=multi-user.target

Change mylinuxuser and mylinuxgroup to whatever user and group you want the server to run as. This should be the same user and group that own the files in the calibre library folder. Note that it is generally not a good idea to run the server as root. Also change the path to the calibre library folder to suit your system. You can add multiple libraries if needed. See the help for the calibre-server command.

Now run:

sudo systemctl start calibre-server

to start the server. Check its status with:

sudo systemctl status calibre-server

To make it start at boot, run:

sudo systemctl enable calibre-server

Not

The calibre server does not need a running X server, but it does need the X libraries installed as some components it uses link against them.

Not

The calibre server also supports systemd socket activation, so you can use that, if needed, as well.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- E-kitapları karşılaştır

E-kitapları karşılaştır
calibre, bir e-kitabı düzenledikten veya dönüştürdükten sonra içinde nelerin değiştiğini görmek için kullanılabilecek entegre bir e-kitap karşılaştırma aracı içerir. EPUB ve AZW3 biçimlerindeki kitapları karşılaştırabilir.

Kullanmak için, e-kitabı araçta düzenle için açın ve ardından Dosya → Başka kitapla karşılaştır`a tıklayın veya :ref:`book_details panelini kullanın. EPUB’dan EPUB’a dönüştürme yaparsanız orijinal EPUB dosyası ORIGINAL_EPUB olarak kaydedilecektir. Kitap ayrıntıları panelinde ORIGINAL_EPUB girişine sağ tıklayın ve :guilabel:`EPUB formatıyla karşılaştır’ı seçin.

Karşılaştırma aracı aşağıdaki ekran görüntüsündeki gibi olacaktır. Seçilen kitaplar arasındaki metin, biçem ve resimlerdeki farklılıkları gösterecektir.

Karşılaştırma görünümünü anlamak

Yukarıdaki ekran görüntüsünden anlaşılabileceği gibi karşılaştırma görünümü iki kitabın farklarını yan yana göstermektedir. Sadece farklar ve etraflarındaki bir kaç satırlık içerik görüntülenir. Bu sayede büyük bir belgenin içinde neyin değiştiğini anlamak kolaylaşır.

Eklenen metin yeşil, çıkartılan metin kırmızı ve değiştirilen metinler de mavi arkaplanla gösterilir.

Değiştirilen satırların numaraları düzenleyici ile değişiklik yapılmasını sağlamak için yanlarında görüntülenir. Karşılaştırma aracını düzenleyicinin içinde açtığınızda sağ paneldeki bir satıra çift tıklayarak düzenleyicinin otomatik olarak o satıra gitmesini sağlayabilirsiniz.

Kitapları karşılaştırmanın kullanışlı bir tekniği de karşılaştırma aracına farklılıkları hesaplamadan önce metni ve biçem dosyalarını güzelleştirmesini söylemektir. Bu genellikle daha temiz ve takip edilebilir bir çıktı verir. Bunu yapmak için Seçenekler düğmesine tıkladıktan sonra Dosyaları karşılaştırmadan önce güzelleştir seçeneğini seçmelisiniz. Dosyaları güzelleştirmenin bazı istenmeyen sonuçları olabileceğini unutmayın. Farkların etrafında gösterilecek içeriğin satır sayısını da Seçenekler düğmesi ile değiştirebilirsiniz.

Alt kısımdaki Arama çubuğunu kullanarak farklardaki herhangi bir metni arayabilirsiniz. Hangi panelin aranacağını belirtmeniz gerekecektir; Sol veya Sağ.

Karşılaştırma aracı çalıştırılıyor

Karşılaştırma aracı bir kitabın iki sürümü elinizde olduğunda ve nelerin değiştiğini görmek istediğinizde çok faydalı olur. Başlatılmasının bir çok yolu vardır.

İki e-kitap dosyasının karşılaştırılması

İlk dosyayı düzenle aracında açın. Şimdi Dosya → Başka bir kitapla karşılaştır düğmesine tıklayarak ikinci dosyayı seçin (ilkiyle aynı biçimde olmalıdır). Karşılaştırma görünümü düzenlenen dosya sağda, ikinci dosya solda olacak şekilde açılacaktır.

ORIGINAL_FMT ile FMT dosyalarının karşılaştırılması

Calibre içinde bir FMT dosyasından kendisine bir dönüşüm yaparsanız özgün dosya ORIGINAL_FMT adıyla kaydedilir. Dönüşüm sonrası neyin değiştiğini ana calibre penceresindeki Kitap detayları panelinde ORIGINAL_FMT girdisine sağ tıklayıp FMT ile karşılaştır seçeneğini seçerek görebilirsiniz. Karşılaştırma görünümü solda ORIGINAL_FMT dosyasını, sağda da FMT dosyasını açacaktır.

Bir kontrol noktasını ile kitabın mevcut durumunu düzenlerken karşılaştırmak
düzenle aracının kontrol noktaları isimli çok yararlı bir özelliği vardır. Bu özellik kitabın mevcut durumunu kontrol noktası adıyla kaydetmenize imkan verir, kontrol noktasından sonra yaptığınız ve hoşlanmadığınız değişiklikleri geri alabilirsiniz. Kontrol noktaları düzenleyicide otomatikleştirilmiş eylemler gerçekleştirdiğinizde de otomatik olarak oluşturulur. Kontrol noktalarının listesini Görünüm → Kontrol noktaları alanına giderek görebilir ve Karşılaştır düğmesine tıklayarak kitabın mevcut durumunu seçtiğiniz kontrol noktası ile karşılaştırabilirsiniz. Karşılaştırma aracı karşılaştırma noktasını solda, mevcut durumu da sağda görüntüleyecektir.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- E-kitap meta verilerini düzenle

E-kitap meta verilerini düzenle

İçindekiler

- Bir defada bir kitabın üst verisini düzenlemek

- Üst veri indiriliyor

- Kitap biçimlerini yönetmek

- Kapaklar hakkında

- Birden fazla kitabın üst verisini birden değiştirmek

- Ara ve değiştir

- Metadata’nın toplu indirilmesi

- Bir kitaba fazladan veri dosyaları ekleme

E-kitaplar her şekil ve boyutta olabilir ve çoğunlukla meta verileri (başlık/yazar/seri/yayıncı gibi) eksik veya yanlıştır. Calibre’de meta verileri değiştirmenin en basit yolu bir girdiye çift tıklayıp doğru değişimi yazmaktır. Daha karmaşık “güçlü düzenleme” için aşağıda tartışılan meta veri düzenleme araçlarını kullanın.

Bir defada bir kitabın üst verisini düzenlemek

Düzenlemek istediğiniz kitaba tıkladıktan sonra Üst veriyi düzenle düğmesine veya E tuşuna basın. Üst verinin tüm alanlarını düzenleyebileceğiniz bir iletişim penceresi açılacaktır. Düzenlemeyi daha hızlı ve verimli kılacak araçlar barındırır. Sıkça kullanılan bazı ipuçları:

- Başlık ve yazar arasında düğmeye tıklayarak ikisini birden otomatik kaydırabilirsiniz.

- Yazar sıralamasının yanındaki düğmeye tıklayarak calibre’nin otomatik olarak her yazar için sıralama değerleriyle doldurmasını sağlayabilirsiniz. Yazarları yönet iletişim penceresini kullanarak yazarların sıralama değerlerini görebilir ve değiştirebilirsiniz. Bu pencere yazar sıralamasının yanındaki düğmeye tıklayıp basılı tutarak açılabilir.

- Kitapla ilişkili etiketleri yönetmek amacıyla :guilabel:`Etiket düzenleyici`yi kullanmak için etiketlerin yanındaki düğmeye tıklayabilirsiniz.

- “ids” kutusu bir ISBN (ve diğer birçok id türü) girmek için kullanılabilir; geçersiz bir ISBN girerseniz kırmızı bir arka plana sahip olacaktır. Geçerli ISBN’ler için yeşil olacaktır.

- Yazar sıralama kutusu calibre’nin düşündüğünden başka bir değer alırsa kırmızı olacaktır.

Üst veri indiriliyor

Meta verileri düzenle iletişim kutusunun en güzel özelliği, çeşitli web sitelerinden meta veriler alarak birçok meta veri alanını otomatik olarak doldurabilmesidir. Calibre şu anda Google Kitaplar ve Amazon’u kullanıyor. Meta veri indirme işlemi sizin için Başlık, yazar, seri, etiketler, derecelendirme, açıklama ve ISBN alanlarını doldurabilir.

İndirmeyi kullanmak için başlık ve yazar alanlarını doldurduktan sonra Üst veriyi getir düğmesine tıklayın. calibre size en yakın eşleşmelerin olduğu bir liste döndürecektir. Eğer önce ISBN alanını doldurursanız başlığı ve yazarı bulmak için kullanılacaktır. Eşleşme bulunamazsa aramanızda bazı anahtar kelimeleri ve yazarın soyadını kullanarak yeniden yapmayı deneyin.

Kitap biçimlerini yönetmek

Calibre içinde bir kitap girdisi ile ilişkili farklı biçimler bulunabilir. Örneğin Shakespeare’in Bütün Eserlerini EPUB biçiminde bulundurduğunuzu ve sonradan Kindle’ınızda okumak için MOBI biçimine dönüştürdüğünüzü düşünelim. calibre sizin için çoklu biçimleri yönetecektir. Üst veriyi düzenle iletişim penceresindeki Kullanılabilir biçimler bölümünde bu biçimleri yönetebilirsiniz. Yeni biçimler ekleyebilir, mevcut biçimleri silebilir ve calibre’nin biçimlerin birinde üst veriden kapağı almasını atayabilirsiniz.

Kapaklar hakkında

Calibre’den geçerli bir ISBN numarası olan kitapların kapaklarını indirmesini isteyebilirsiniz. Bir diğer seçenek olarak bilgisayarınızdaki bir dosyanın kapak olarak kullanılmasını da sağlayabilirsiniz. Hatta calibre sizin için bir üst veriye sahip varsayılan bir kapak bile üretebilir. Kapağı değiştirmek için üzerine resimler sürükleyip bırakabileceğiniz gibi, resimleri kopyalayıp yapıştırmak için sağ tıklamayı da kullanabilirsiniz.

Buna ek olarak, eğer kapağınız kötü kenarlıklara sahipse kapakları otomatik olarak kırpmak için de bir düğme bulunmaktadır.

Birden fazla kitabın üst verisini birden değiştirmek

Öncelikle Ctrl veya Shift tuşunu basılı tutarak üzerlerine tıklayarak düzenlemek istediğiniz kitapları seçin. Birden fazla kitap seçerseniz, Meta verileri düzenle düğmesine tıklamak Toplu meta veri düzenleme iletişim kutusunun açılmasına neden olur. Bu iletişim kutusunu kullanarak, bir grup kitabın yazarını/yayıncısını/derecelendirmesini/etiketlerini/serisini vb. hızlı bir şekilde aynı değere ayarlayabilirsiniz. Bu, bazı ortak meta verilere sahip birkaç kitabı yeni içe aktardıysanız özellikle kullanışlıdır. Bu iletişim kutusu çok güçlüdür; örneğin, meta veriler üzerinde toplu işlemler gerçekleştirmek ve hatta meta verileri bir sütundan diğerine kopyalamak için kullanabileceğiniz bir Ara ve değiştir sekmesine sahiptir.

Normal meta veri düzenleme iletişim kutusunda ayrıca birkaç kitabın meta verilerini birbiri ardına düzenlemek için kullanabileceğiniz Sonraki ve Önceki düğmeleri bulunur.

Ara ve değiştir

Birçok kitabın meta verilerini düzenle’ iletişim kutusu, seçilen kitaplar üzerinde isteğe bağlı olarak güçlü arama ve değiştirme işlemleri gerçekleştirmenize olanak tanır. Varsayılan olarak basit bir metin arama ve değiştirme işlevini kullanır, ancak aynı zamanda *normal ifadeleri* de destekler. Düzenli ifadeler hakkında daha fazla bilgi için bkz. :ref:`regexptutorial.

Yukarıda belirtildiği gibi iki arama ve değiştirme modu vardır: karakter eşleştirme ve düzenli ifade. Karakter eşleşmesi, ‘arama’ kutusuna yazdığınız karakterler için seçtiğiniz ‘Arama alanı’nda görünecek ve bu karakterleri, ‘ile değiştir’ kutusuna yazdığınız karakterlerle değiştirecektir. Arama karakterlerinin alandaki her tekrarı değiştirilecektir. Örneğin, aranan alanın ‘kötü kedi’ içerdiğini varsayalım. ‘A’ yerine ‘HELLO’ yazılmasını ararsanız sonuç ‘HELLO bHELLOd cHELLOt’ olur.

Aradığınız alan etiketler gibi çoklu bir alansa, her etikete ayrı ayrı davranılır. Örneğin etiketiniz Korku,Korkutucu içeriyorsa, arama ifadesi r herhangi bir şeyle eşleşmeyecektir, çünkü ifade önce Korku ardından Korkutucu ya uygulanır.

Aramanın büyük/küçük harfe duyarsız olmasını istiyorsanız, Büyük küçük harf duyarlı kutusundan işareti kaldırın.

Calibre’nin sonucun harflerini büyütmesini ya da küçütmesini isterseniz (yer değiştirme gerçekleştikten sonraki bilgi) Yer değiştirmeden sonra fonksiyon uygula kutusundaki fonksiyonlardan birini seçebilirsiniz. Kullanılabilir işlemler şunlardır:

- Küçük harf – alandaki tüm karakterleri küçük harfe çevir

- Büyük harf – alandaki tüm karakterleri büyük harfe çevir

- Başlık boyutu – Sonuçtaki tüm kelimelerin ilk harfini büyüt.

Sizin testiniz kutusu arama/yer değiştirmenin yapmasını istediğiniz şeyi yapıp yapmadığını kontrol edebilmek için metin girebilmenizi sağlar. Çoğu durumda bu kutular yeterli olur, ama bazı durumlarda kontrol etmek istediğiniz durum bu kutularda gösterilmemiş olabilir. Bu gibi durumları `Sizin testiniz`e girin.

Düzenli ifade kipi karakter kipinden düzenli ifadeler kullanmaktan ötede (tabi ki) biraz farklıdır. Öncelikle fonksiyonlar metnin arama metniyle eşleşen kısımlarına uygulanır, tüm alana değil. İkincisi fonksiyonlar yer değiştirme karakter dizisine uygulanır, tüm alana değil.

Üçüncü ve en önemlisi yer değiştirme karakter dizisi geri referanslar kullanarak arama dizisinin bazı kısımlarına referans verebilir. Geri referans şudur \\n burada n arama ifadesindeki n’inci parantezli gruba işaret eden bir tam sayıdır. Örneğin yukardaki aynı örnek kullanıldığında, kötü bir kedi, arama ifadesi kötü (…) (…), ve yer değiştirme ifadesi kötü \2 \1 ile sonuç kötü kedi bir olur. Geri referanslarla ilgili daha fazla bilgi için :ref:`regexptutorial`a göz atın.

Yararlı bir model: Tüm alanın büyük/küçük harflerini değiştirmek istediğinizi varsayalım. Bunu yapmanın en kolay yolu karakter modunu kullanmaktır, ancak normal ifade modunu kullanmak istediğinizi de varsayalım. Arama ifadesi (^.*$), değiştirme ifadesi \1 olmalı ve istenilen büyük/küçük harf değiştirme fonksiyonu seçilmelidir.

Son olarak, düzenli ifade kipinde değerleri bir alandan diğerine kopyalayabilirsiniz. Basitçe kaynak ve hedef alanları farklı yapın. Kopyalama hedef alanın yerini değiştirebilir, bir alanın başına ekleyebilir (önüne), veya alanın sonuna ekleyebilir (arka ucuna). ‘virgül kullan’ kontrol kutusu calibre’ye öne ve sona ekleme kiplerinde metin ve hedef alan arasına virgül koymasını (veya koymamasını) söyler. Hedef çoklu ise (örn., etiketler), bu kutuyu işaretlemek zorundasınız.

Ara ve yer değiştir diğer sekmelerdeki diğer tüm metadata değişiklikleri uygulandıktan sonra yapılır. Bu kafaları karıştırabilir, çünkü test kutuları diğer değişikliklerden önceki bilgiyi gösterir, ama işlem değişikliklerden sonra uygulanır. Ne olacağıyla ilgili şüpheleriniz varsa, arama/yer değiştirmeyi başka değişikliklerle karıştırmayın.

Metadata’nın toplu indirilmesi

Birden çok kitap için metadata’yı topluca indirmek istiyorsanız, Metadata düzenle düğmesine sağ tıklayın ve Metadata indir seçin. Yalnızca metadata, yalnızca kapaklar veya ikisini de indirmeyi seçebilirsiniz.

Bir kitaba fazladan veri dosyaları ekleme
calibre bir kitapla ilişkili herhangi bir sayıda ekstra veri dosyasını saklayabilir. Bunlar alternatif kapaklar, ek materyaller vb. olabilir. Doğrudan görüntülenemez veya dönüşüm kaynağı olarak kullanılamazlar. Calibre’de Tam metin arama motoru tarafından da indekslenmezler. Bunları görüntülemek/eklemek/silmek için kitabı seçin ve Meta verileri düzenle düğmesine sağ tıklayın ve Veri dosyalarını yönet`i seçin. Bu, bu dosyalar üzerinde işlem gerçekleştirebileceğiniz bir pencere açılacaktır. Alternatif olarak, veri dosyalarını daha hızlı eklemek için :guilabel:`Kitap ekle düğmesine sağ tıklayıp :guilabel:`Seçili kitap kayıtlarına veri dosyaları ekle`yi seçebilirsiniz.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Sıkça Sorulan Sorular

Sıkça Sorulan Sorular

İçindekiler

- E-book format conversion

- Device integration

- Library management

- Muhtelif

E-book format conversion

İçindekiler

- Calibre hangi biçimlerde dönüştürmeyi destekler?

- Dönüştürülecek en iyi kaynak biçimleri nelerdir?

- Bir PDF dosyası dönüştürdüm, ama sonuçta çeşitli sorunlar var?

- İngiliz alfabesi dışında karakterler, veya akıllı tırnak içeren dosyamı nasıl dönüştürürüm?

- MOBI dosyalarındaki İçindekiler meselesi nedir?

- Toplu HTML dosyalarını belirli bir sırada nasıl dönüştürürüm?

- Calibre ile ürettiğim EPUB geçerli değil?

- Dönüştürme araçlarının bazı gelişmiş özelliklerini nasıl kullanırım?

Calibre hangi biçimlerde dönüştürmeyi destekler?

calibre bir çok girdi biçiminden çıktı biçimine dönüştürmeyi destekler. Aşağıdaki listedeki tüm girdi biçimlerini tüm çıktı biçimlerine dönüştürebilir.

Input Formats: AZW, AZW3, AZW4, CBZ, CBR, CB7, CBC, CHM, DJVU, DOCX, EPUB, FB2, FBZ, HTML, HTMLZ, LIT, LRF, MOBI, ODT, PDF, PRC, PDB, PML, RB, RTF, SNB, TCR, TXT, TXTZ

Çıkış formatları: AZW3, EPUB, DOCX, FB2, HTMLZ, OEB, LIT, LRF, MOBI, PDB, PMLZ, RB, PDF, RTF, SNB, TCR, TXT, TXTZ, ZIP

Not

PRC is a generic format, calibre supports PRC files with TextRead and MOBIBook headers. PDB is also a generic format. calibre supports eReader, Plucker (input only), PML and zTxt PDB files. DJVU support is only for converting DJVU files that contain embedded text. These are typically generated by OCR software. MOBI books can be of two types Mobi6 and KF8. calibre fully supports both. MOBI files often have .azw or .azw3 file extensions. DOCX files from Microsoft Word 2007 and newer are supported.

Dönüştürülecek en iyi kaynak biçimleri nelerdir?

In order of decreasing preference: LIT, MOBI, AZW, EPUB, AZW3, FB2, FBZ, DOCX, HTML, PRC, ODT, RTF, PDB, TXT, PDF

Bir PDF dosyası dönüştürdüm, ama sonuçta çeşitli sorunlar var?

PDF dönüştürmek için berbat bir biçim. PDF dönüştürürken karşılaşabileceğiniz çeşitli sorunlar için, bknz: PDF belgelerini dönüştür.

İngiliz alfabesi dışında karakterler, veya akıllı tırnak içeren dosyamı nasıl dönüştürürüm?

Bu probleme iki bakış açısı vardır:

1. Knowing the encoding of the source file: calibre tries to guess what character encoding your source files use, but often, this is impossible, so you need to tell it what encoding to use. This can be done in the GUI via the Input character encoding field in the Look & feel → Text section of the conversion dialog. The command-line tools have an ebook-convert-txt-input --input-encoding option.

2. When adding HTML files to calibre, you may need to tell calibre what encoding the files are in. To do this go to Preferences → Advanced → Plugins → File type and customize the HTML to ZIP plugin, telling it what encoding your HTML files are in. Now when you add HTML files to calibre they will be correctly processed. HTML files from different sources often have different encodings, so you may have to change this setting repeatedly. A common encoding for many files from the web is cp1252 and I would suggest you try that first. Note that when converting HTML files, leave the input encoding setting mentioned above blank. This is because the HTML to ZIP plugin automatically converts the HTML files to a standard encoding (UTF-8).

MOBI dosyalarındaki İçindekiler meselesi nedir?

The first thing to realize is that most e-books have two tables of contents. One is the traditional Table of Contents, like the ToC you find in paper books. This Table of Contents is part of the main document flow and can be styled however you like. This ToC is called the content ToC.

Then there is the metadata ToC. A metadata ToC is a ToC that is not part of the book text and is typically accessed by some special button on a reader. For example, in the calibre E-book viewer, you use the Show Table of Contents button to see this ToC. This ToC cannot be styled by the book creator. How it is represented is up to the viewer program.

In the MOBI format, the situation is a little confused. This is because the MOBI format, alone amongst mainstream e-book formats, does not have decent support for a metadata ToC. A MOBI book simulates the presence of a metadata ToC by putting an extra content ToC at the end of the book. When you click Goto Table of Contents on your Kindle, it is to this extra content ToC that the Kindle takes you.

Şimdi size MOBI kitabının iki tane aynı İçindekileri var gibi gelebilir. Birinin anlambilimsel İçerik diğerinin metadata İçeriği olduğunu hatırlayın, her ikisi aynı görünse ve aynı içeriğe sahip olsa da. Biri Kindle menüsünden doğrudan erişilebilir, diğeri erişilemez.

When converting to MOBI, calibre detects the metadata ToC in the input document and generates an end-of-file ToC in the output MOBI file. You can turn this off by an option in the MOBI Output settings. You can also tell calibre whether to put it at the start or the end of the book via an option in the MOBI Output settings. Remember this ToC is semantically a metadata ToC, in any format other than MOBI it cannot not be part of the text. The fact that it is part of the text in MOBI is an accident caused by the limitations of MOBI. If you want a ToC at a particular location in your document text, create one by hand. So we strongly recommend that you leave the default as it is, i.e. with the metadata ToC at the end of the book. Also note that if you disable the generation of the end-of-file ToC the resulting MOBI file may not function correctly on a Kindle, since the Kindle’s use the metadata ToC for many things, including the Page Flip feature.

Girdi belgesinde elle düzenlenmiş bir İçindekileriniz varsa, calibre’deki İçindekiler algılama seçeneğini kullanarak metadata İçindekileri otomatik olarak oluşturabilirsiniz. Bu seçeneklerin kullanımına göz atmak için Kullanıcı Kılavuzundaki dönüştürme kısmına bakın.

Finally, I encourage you to ditch the content ToC and only have a metadata ToC in your e-books. Metadata ToCs will give the people reading your e-books a much superior navigation experience (except on the Kindle, where they are essentially the same as a content ToC).

Not

Yeni AZW3 biçimi metadata içindekileri düzgün destekler. Ancak, Kindle yazılımı dosya sonu satır arası İçindekileri kapatırsanız yanlış çalışmaya meyillidir. Yani üretilen İçindekilere dokunmamanız önerilir. Metadata İçindekileri olan bir AZW3 dosyası oluşturur ve üretilen dosya sonu İçindekileri olmazsa, Kindle üzerinde bazı özellikler çalışmaz, Sayfa Çevirme özelliği gibi.

Toplu HTML dosyalarını belirli bir sırada nasıl dönüştürürüm?

Toplu HTML dosyalarını belirli sırada dönüştürmek için bir İçindekiler dosyası oluşturmalısınız. Bu, diğer tüm bu dosyalara sıralı bağlantılar içeren başka bir HTML dosyasıdır. Bu tarz bir dosya şuna benzer:

<html>
<body>
<h1>Table of Contents</h1>
<p style="text-indent:0pt">
<a href="file1.html">First File</a><br/>
<a href="file2.html">Second File</a><br/>
.
.
.
</p>
</body>
</html>

Then, just add this HTML file to the GUI and use the Convert button to create your e-book. You can use the option in the Table of Contents section in the conversion dialog to control how the Table of Contents is generated.

Not

By default, when adding HTML files, calibre follows links in the files in depth first order. This means that if file A.html links to B.html and C.html and D.html, but B.html also links to D.html, then the files will be in the order A.html, B.html, D.html, C.html. If instead you want the order to be A.html, B.html, C.html, D.html then you must tell calibre to add your files in breadth first order. Do this by going to Preferences → Advanced → Plugins → File type and customizing the HTML to ZIP plugin.

Calibre ile ürettiğim EPUB geçerli değil?

calibre kendisi tarafından üretilen bir EPUB’ın geçerli olacağını garanti etmez. Garanti ettiği tek şey geçerli XHTML 1.1 + CSS 2.1 ile beslerseniz geçerli bir EPUB üreteceğidir. calibre ürettiği EPUB’ların geniş yelpazede cihazlarda istenilen şekilde çalıştığını garantilemek ister, bu da geçerli EPUB’lar üretmekle uyumlu değildir, ve kullanıcılarının büyük çoğunluğu için önemli olan budur. Her zaman geçerli EPUB üreten bir araca ihtiyacınız varsa calibre size göre değildir. Bunun anlamı, calibre tarafından oluşturulmuş bir EPUB’ı EPUB doğrulama kontrolü yapan çevrimiçi bir mağazaya göndermek isterseniz EPUB’ın geçerliliğini kontrol etme sorumluluğunun sizde olduğudur, calibre bunu sizin için yapmaz – başka bir deyişle calibre’ye girdi belgesi olarak geçerli XHTML + CSS vermelisiniz.

Dönüştürme araçlarının bazı gelişmiş özelliklerini nasıl kullanırım?

You can get help on any individual feature of the converters by mousing over it in the GUI or running ebook-convert dummy.html .epub -h at a terminal. A good place to start is to look at the following demo file that demonstrates some of the advanced features html-demo.zip [https://calibre-ebook.com/downloads/html-demo.zip].

Device integration

İçindekiler

- Calibre hangi aygıtları destekliyor?

- Aygıtımın calibre tarafından desteklenmesi konusunda nasıl yardım alabilirim?

- Aygıtım calibre tarafından algılanmıyor?

- Aygıtım standart dışı veya nadir. Bağlamak için ne yapabilirim?

- Calibre’yi iPad/iPhone/iPod touch ile nasıl kullanırım?

- How do I use calibre with my Android phone/tablet or Kindle Fire?

- Kindle veya başka bir okuyucu aygıttaki web tarayıcıyı kullanarak calibre kitaplarıma erişebilir miyim?

- Calibre kullanarak e-posta gönderemiyorum?

- My device is getting mounted read-only in Linux, so calibre cannot connect to it?

- Calibre neden Kindle koleksiyonlarını veya Nook raflarını desteklemiyor?

- Calibre’yi Kobo Touch/Glo/vs. ile kullanırken hata alıyorum?

- Covers for books I send to my e-ink Kindle show up momentarily and then are replaced by a generic cover?

- MOBI dosyaları için kapaklar PC için Kindle/Android için Kindle/iPad vs. de görünmemeye başladı.

- Calibre kullanarak bazı kitapları Kindle’me yükledim ama görünmüyorlar?

Calibre hangi aygıtları destekliyor?

calibre can directly connect to all the major (and most of the minor) e-book reading devices, smartphones, tablets, etc. In addition, using the Connect to folder function you can use it with any e-book reader that exports itself as a USB disk. Finally, you can connect wirelessly to any device that has a web browser using the calibre Content server.

Aygıtımın calibre tarafından desteklenmesi konusunda nasıl yardım alabilirim?

Aygıtınız işletim sistemine USB disk olarak görünüyorsa calibre desteği çok kolaydır. Sizden yalnızca bazı bilgiler isteyeceğiz:

- Complete list of e-book formats that your device supports.

- Is there a special folder on the device in which all e-book files should be placed? Also does the device detect files placed in sub-folders?

- Ayrıca sizden calibre’nin otomatik toplayacağı aygıtınızla ilgili bilgiler isteyeceğiz. Öncelikle, aygıtınız SD kart destekliyorsa, takın. Aygıtı bilgisayara takın. Calibre’de Tercihler → Çeşitli menüsüne gidin, “Aygıt algılamayı ayıkla” düğmesine tıklayın. Bu bir miktar hata ayıklama çıktısı oluşturacaktır. Bir dosyaya kopyalayın ve işlemi tekrarlayın, bu sefer aygıtınız bilgisayardan çıkarılmış olsun.

- Yukarıdaki her iki çıktıyı gerekli başka bilgilerle bize gönderin, aygıtınız için aygıt sürücüsü yazacağız.

Once you send us the output for a particular operating system, support for the device in that operating system will appear in the next release of calibre. To send us the output, open a bug report and attach the output to it. See how to report bugs [https://calibre-ebook.com/bugs].

Aygıtım calibre tarafından algılanmıyor?

Sorunu bulmak için şu adımları takip edin:

- Bilgisayarınıza aynı anda tek bir aygıt taktığınızdan emin olun. Calibre destekli başka bir aygıt iPhon/iPad vs. aynı anda takmayın.

- If you are connecting an Apple iDevice (iPad, iPod Touch, iPhone), Apple no longer allows third party software to connect to their devices using a USB cable. Instead use a wireless connection, via the calibre Content server.

- If you are connecting a Kindle Fire or other Android device, read the note under USB kablo kullanımı.

- On macOS if you get permission errors when connecting a device to calibre, you can fix that by looking under System Preferences > Security and Privacy > Privacy > Files and Folders.

- Make sure you are running the latest version of calibre (currently 7.10.0). The latest version can always be downloaded from the calibre website [https://calibre-ebook.com/download]. You can tell what version of calibre you are currently running by looking at the bottom line of the main calibre window.

- Ensure your operating system is seeing the device. That is, the device should show up in Windows Explorer (in Windows) or Finder (in macOS).

- Calibre’de, Tercihler → Yoksayılan Aygıtlar a gidin ve aygıtınızın göz ardı edilmediğinden emin olun

- Tüm bu adımlar başarısız olursa, Tercihler → Çeşitli ye gidin ve aygıtınız takılıyken Aygıt tanımlamada hata ayıkla ya tıklayın ve çıktıyı bir bilet olarak calibre hata takibine [https://bugs.launchpad.net/calibre] gönderin.

Aygıtım standart dışı veya nadir. Bağlamak için ne yapabilirim?

In addition to the Connect to folder function found under the Connect/share button, calibre provides a User defined device plugin that can be used to connect to any USB device that shows up as a disk drive in your operating system. Note: on Windows, the device must have a drive letter for calibre to use it. See the device plugin Preferences -> Plugins -> Device plugins -> User defined and Preferences -> Miscellaneous -> Get information to setup the user defined device for more information. Note that if you are using the user defined plugin for a device normally detected by a builtin calibre plugin, you must disable the builtin plugin first, so that your user defined plugin is used instead.

Calibre’yi iPad/iPhone/iPod touch ile nasıl kullanırım?

An easy way to browse your calibre collection from your Apple device is by using Calibre İçerik sunucusu, which makes your collection available over the net. First perform the following steps in calibre

- Calibre’de Tercih Edilen Çıktı Biçimini EPUB olarak ayarlayın (Çıktı biçimi Tercihler → Arayüz → Davranış altından ayarlanabilir)

- Set the output profile to iPad (this will work for iPhone/iPods as well), under Preferences → Conversion → Common options → Page setup

- Convert the books you want to read on your iDevice to EPUB format by selecting them and clicking the Convert button.

- Turn on the Content server by clicking the Connect/share button and leave calibre running. You can also tell calibre to automatically start the Content server via Preferences → Sharing → Sharing over the net.

The Content server allows you to read books directly in Safari itself. In addition, there are many apps for your iDevice that can connect to the calibre Content server. Examples include: Marvin, Mapleread and iBooks itself.

Using the Content server

Safari tarayıcıyı başlatın ve calibre sunucuyu çalıştıran bilgisayarın IP adresini ve portunu girin, şu şekilde:

http://192.168.1.2:8080/

Replace 192.168.1.2 with the local IP address of the computer running calibre. See Calibre İçerik sunucusu for details on running the server and finding out the right IP address to use.

You will see a list of books in Safari, tap on any book and you will be given the option to either download it, or read it in the browser itself. If you choose to download it, Safari will ask you if you want to open it with iBooks.

Many reading apps support browsing the calibre library directly via its OPDS support. In such apps you can go to the online catalog screen and add the IP address of the calibre server to browse and download books from your calibre library within the app.

How do I use calibre with my Android phone/tablet or Kindle Fire?

There are two ways that you can connect your Android device to calibre. Using a USB cable – or wirelessly, over the air. The first step to using an Android device is installing an e-book reading application on it. There are many free and paid e-book reading applications for Android: Some examples (in no particular order): FBReader [https://play.google.com/store/apps/details?id=org.geometerplus.zlibrary.ui.android&hl=en], Moon+ [https://play.google.com/store/apps/details?id=com.flyersoft.moonreader&hl=en], Mantano [https://play.google.com/store/apps/details?id=com.mantano.reader.android.lite&hl=en], Aldiko [https://play.google.com/store/apps/details?id=com.aldiko.android&hl=en], Kindle [https://play.google.com/store/apps/details?id=com.amazon.kindle&feature=related_apps].

USB kablo kullanımı

Simply plug your device into the computer with a USB cable. calibre should automatically detect the device and then you can transfer books to it by clicking the Send to device button. Note that on macOS and Linux only a single program can connect to an Android device at a time, so make sure the device is not opened in the OS File manager, or the Android File Transfer utility, etc.

Not

With newer Android devices, you might have to jump through a few hoops to get the connection working, as Google really does not want you to be independent of its cloud. First, unlock the screen before plugging in the USB cable. When you plugin in the USB cable you will get a popup notification. Make sure it says some thing like “Transferring Media files” or “MTP (Media Transfer mode)”. If it does not, tap the notification, and change the mode to Media Transfer (MTP). You may need to restart calibre at this point in order for your device to be recognized. Finally, you might get a popup on the device every time calibre or the operating system actually tries to connect to it, asking for permission, tap OK.

Not

With the Kindle Fire 8 or newer there is an icon that shows up when the USB cable is plugged in, showing that the device is charging. Tap that and switch the device to data transfer mode, and then start calibre, it should then be detected.

Kablosuz olarak
calibre has a builtin web server, the Calibre İçerik sunucusu. It makes your calibre collection available over the net. You can browse it on your device using a simple browser or a dedicated application. First perform the following steps in calibre:

- calibre’de Tercih Edilen Çıktı Biçimi`ni normal Android aygıtlar için EPUB veya Kindle’ler için MOBI olarak ayarlayın (Çıktı biçimi :guilabel:`Tercihler → Arayüz → Davranış altından ayarlanabilir)

- Convert the books you want to read on your device to EPUB/MOBI format by selecting them and clicking the Convert button.

- Turn on the Content server in calibre’s preferences and leave calibre running.

Android aygıtınızda, tarayıcıyı açın ve şu adrese gidin

http://192.168.1.2:8080/

Replace 192.168.1.2 with the local IP address of the computer running calibre. See Calibre İçerik sunucusu for details on running the server and finding out the right IP address to use.

You can now browse your book collection and download books from calibre to your device to open with whatever e-book reading software you have on your Android device.

Many reading apps support browsing the calibre library directly via its OPDS support. In such apps you can go to the online catalog screen and add the IP address of the calibre server to browse and download books from your calibre library within the app.

Kindle veya başka bir okuyucu aygıttaki web tarayıcıyı kullanarak calibre kitaplarıma erişebilir miyim?

calibre has a Content server that exports the books in calibre as a web page. See Calibre İçerik sunucusu for details.

Some devices, like the Kindle (1/2/DX), do not allow you to access port 8080 (the default port on which the content server runs). In that case, change the port in the calibre Preferences to 80. (On some operating systems, you may not be able to run the server on a port number less than 1024 because of security settings. In this case the simplest solution is to adjust your router to forward requests on port 80 to port 8080).

Also some devices do not have browsers advanced enough to run the app-like interface used by the Content server. For such devices, you can simply add /mobile to the server URL to get a simplified, non-JavaScript interface.

Calibre kullanarak e-posta gönderemiyorum?

Because of the large amount of spam in email, sending email can be tricky, as different mail servers use different strategies to block email. The most common problem is if you are sending email directly (without a mail relay) in calibre. Many servers (for example, Amazon) block email that does not come from a well known relay. The most robust way to setup email sending in calibre is to do the following:

- Create a free GMX account at GMX [https://www.gmx.com].

- Goto Preferences → Sharing → Sharing books by email in calibre and click the Use GMX button and fill in the information asked for.

- Log into your GMX account on the website and enable SMTP sending (Settings->POP3 & IMAP->Send and receive emails via external program)

- calibre will then be able to use GMX to send the mail.

- If you are sending to your Kindle, remember to update the email preferences on your Amazon Kindle page to allow email sent from your GMX email address. Also note that Amazon does not allow email delivery of AZW3 and new style (KF8) MOBI files. Finally, Amazon recently started sending confirmation emails that you have to click on back to your GMX account before the book is actually delivered.

Bunu yaptıktan sonra bile, problemler yaşayabilirsiniz. Yaygın problem kaynaklarından biri kötü tasarlanmış bir anti virüs programının calibre’nin e-posta gönderimi için açtığı bağlantıları engellemesidir. Anti virüs programınızda calibre için bir istisna koymayı deneyin.

Not

Microsoft/GMX can disable your account if you use it to send large amounts of email. So, when using these services to send mail calibre automatically restricts itself to sending one book every five minutes. If you don’t mind risking your account being blocked you can reduce this wait interval by going to Preferences → Advanced → Tweaks in calibre.

Not

Google recently deliberately broke their email sending protocol (SMTP) support in an attempt to force everyone to use their web interface so they can show you more ads. They are trying to claim that SMTP is insecure, that is incorrect and simply an excuse. Use some other email provider instead.

Not

If you are concerned about giving calibre access to your email account, simply create a new free email account with GMX or Outlook and use it only for calibre.

My device is getting mounted read-only in Linux, so calibre cannot connect to it?

Linux çekirdekleri aygıtları dosya sistemlerinde hata olduğunda yalnızca okunur bağlar. Dosya sistemini şöyle düzeltebilirsiniz:

sudo fsck.vfat -y /dev/sdc

/dev/sdc’yi aygıtınızın aygıt düğüm yoluyla değiştirin. Aşağıdaki çıktıya göz atarak /dev altındaki aygıt düğümünüzü bulabilirsiniz:

mount

Calibre neden Kindle koleksiyonlarını veya Nook raflarını desteklemiyor?

Neither the Kindle nor the Nook provide any way to manipulate collections over a USB connection. If you really care about using collections, I would urge you to sell your Kindle/Nook and get a Kobo. Only Kobo seems to understand that life is too short to be entering collections one by one on an e-ink screen 😇

Note that in the case of the Kindle, there is a way to manipulate collections via USB, but it requires that the Kindle be rebooted every time it is disconnected from the computer, for the changes to the collections to be recognized. As such, it is unlikely that any calibre developers will ever feel motivated enough to support it. There is however, a calibre plugin that allows you to create collections on your Kindle from the calibre metadata. It is available from here [https://www.mobileread.com/forums/showthread.php?t=244202].

Not

Amazon Kindle Touch ve Kindle Fire gibi yeni modellerinde koleksiyonların değiştirilmesi yetisini tamamen kaldırdı, böylece Kindle’nizi kırıp özel yazılım yüklemediğiniz sürece yukardaki eklenti de kullanışsız olacaktır.

Calibre’yi Kobo Touch/Glo/vs. ile kullanırken hata alıyorum?

Kobo çok hatalı bir yazılıma sahip. Bağlantıların rastgele zamanlarda başarısız olduğu biliniyor. Bazı anakart, USB port/kablo/hub kombinasyonları bu başarısızlığı tetikliyor olabilir. Touch aygıtınıza bağlanırken hata alıyorsanız aşağıdakini deneyin, bunlar bazı calibre kullanıcıları için bazen sorunları çözmüş teknikler.

- Kobo’yu bilgisayara doğrudan bağlayın, USB Hub kullanmadan

- Farklı bir USB kablosu ve bilgisayarınızdaki farklı bir USB portunu kullanın

- Log out of the Kobo and log in again, this causes it to rebuild the database, fixing corrupted database errors.

- Kobo Touch yazılımınızı son sürüme güncellemeye çalışın

- Kobo’yu sıfırlamayı deneyin (bazen bu sorunu bir süreliğine çözer, ardından sorun tekrarlanabilir, böylece tekrar tekrar sıfırlamanız gerekiyor)

- Kobo’ya yalnızca bir iki kitap koyarak kullanmayı deneyin, büyük kitap koleksiyonlarından kaçının

Covers for books I send to my e-ink Kindle show up momentarily and then are replaced by a generic cover?

This happens because of an Amazon bug. They try to download a cover for the book from their servers and when that fails, they replace the existing cover that calibre created with a generic cover. For details see this forum thread [https://www.mobileread.com/forums/showthread.php?t=329945]. As of version 4.17, calibre has a workaround, where if you connect the Kindle to calibre after the covers have been destroyed by Amazon, calibre will restore them automatically. So in order to see the covers on your Kindle, you have to:

1. Send the book to the Kindle with calibre

2. Disconnect the Kindle and wait for Amazon to destroy the cover

3. Reconnect the Kindle to calibre

Note that this workaround only works for books sent with calibre 4.17 or later. Alternately, simply keep your Kindle in airplane mode, you don’t really want Amazon knowing every book you read anyway. I encourage you to contact Amazon customer support and complain loudly about this bug. Maybe Amazon will listen.

Not

If the workaround is not working for you make sure you Kindle firmware is at least version 5.12.5, released in April 2020.

MOBI dosyaları için kapaklar PC için Kindle/Android için Kindle/iPad vs. de görünmemeye başladı.

This is caused by a bug in the Amazon software. You can work around it by going to Preferences → Conversion → Output Options → MOBI output and setting the Enable sharing of book content option. If you are reconverting a previously converted book, you will also have to enable the option in the conversion dialog for that individual book (as per book conversion settings are saved and take precedence).

Bunu yapmanın Kindle Fire’da üretilen MOBI’yi Kitaplar yerine kişisel belgeler altında göstereceğine dikkat edin ayrıca Amazon whispersync çalışmayacaktır, ama kapaklar çalışır. Hangi işlevin daha önemli olduğuna siz karar vermelisiniz. Amazon’la iletişime geçip bu hatayı düzeltmelerini söylemenizi öneririm.

The bug in Amazon’s software is that when you put a MOBI file on a Kindle, unless the file is marked as a Personal document, Amazon assumes you bought the book from it and tries to download the cover thumbnail for it from its servers. When the download fails, it refuses to fallback to the cover defined in the MOBI file. This is likely deliberate on Amazon’s part to try to force authors to sell only through them. In other words, the Kindle only displays covers for books marked as Personal Documents or books bought directly from Amazon.

Bir MOBI dosyasını bir e-mürekkep Kindle cihaza USB bağlantısıyla gönderirseniz, calibre bu Amazon hatasını kendisi bir kapak küçük resmi yükleyerek çözer. Ancak, bu çözüm yalnızca USB bağlantısıyla ve calibre ile gönderilirken çalışır. E-posta ile gönderirseniz, Amazon MOBI dosyayı otomatik olarak Kişisel Belge olarak işaretler ve kapak çalışmasına rağmen, kitap Kişisel Belgelerde görüntülenir.

Calibre kullanarak bazı kitapları Kindle’me yükledim ama görünmüyorlar?

Kindle’ye gönderilen kitaplar ancak Kindle tarafından *indis*lendiklerinde görünürler. Bu biraz zaman alabilir. Kitap bir süre sonra hala görünmüyorsa, Kindle indisleyici çökmüş olabilir. Bazen belirli bir kitap çökmeye sebep olmuş olabilir. Malesef, Amazon hangi kitabın çökmeye neden olduğuyla ilgili bir bilgi sağlamaz. Yapabileceğiniz tek şey Kindle’yi baştan başlatmak, veya Windows Gezgini (veya hangi dosya yöneticisini kullanıyorsanız) ile hafızasından tüm dosyaları silerek kitapları teker teker, sorun yaratan kitabı bulana kadar göndermektir. Sorunlu kitabı bulduktan sonra, kitabı Kindle’den silin, calibre ile MOBI’den MOBI’ye veya MOBI’den AZW3’e dönüştürme yapın ve tekrar gönderin. Bu muhtemelene sorunu çözecektir.

Library management

İçindekiler

- Kitap dosyaları nerede saklanıyor?

- Calibre yazar adlarını ve sıralamayı nasıl yönetiyor?

- Why doesn’t calibre let me store books in my own folder structure?

- Neden calibre’nin x için sütunu yok?

- Biçimleri veya ISBN’i gönderek bir sütunum olabilir mi?

- Calibre kitaplığı verimi bir bilgisayardan diğerine nasıl aktarabilirim?

- Calibre’deki kitap listesi boş!

- Bir ağ sürücüsünde/NAS’da calibre kitaplığımla ilgili hatalar alıyorum?

Kitap dosyaları nerede saklanıyor?

Calibre’yi ilk çalıştırdığınızda, kitapları nereye saklamak istediğinizi belirterek bir dizin sorar. Calibre’ye her kitap eklediğinizde, kitabı bu dizine kopyalar. Dizindeki kitaplar güzelce Yazar ve Başlık alt dizinlerine düzenlenir. Bu dizinin içeriği calibre tarafından otomatik yönetilir, bu dizine elle herhangi bir dosya/dizin eklemeyin, otomatik olarak silinebilirler. Bir kitapla ilişkili bir dosyayı eklemek isterseniz, sağ üst köşedeki Metadata düzenle iletişim penceresini kullanarak yapın. Böylece calibre bu dosyayı otomatik olarak düzgün dizine koyup yazar/başlık değiştiğinde taşıma işini halleder.

Metadata about the books is stored in the file metadata.db at the top level of the library folder. This file is a sqlite database. When backing up your library make sure you copy the entire folder and all its sub-folders.

The library folder and all its contents make up what is called a calibre library. You can have multiple such libraries. To manage the libraries, click the calibre icon on the toolbar. You can create new libraries, remove/rename existing ones and switch between libraries easily.

Kitaplıklar arası kitap kopyalamayı (birden fazla kitaplık ayarladığınızda) kitaba sağ tıklayıp Kitaplığa kopyala eylemini seçerek yapabilirsiniz.

Calibre yazar adlarını ve sıralamayı nasıl yönetiyor?

Author names are complex, especially across cultures, see this note [https://www.w3.org/International/questions/qa-personal-names.en.php?changelang=en] for some of the complexities. calibre has a very flexible strategy for managing author names. The first thing to understand is that books and authors are separate entities in calibre. A book can have more than one author, and an author can have more than one book. You can manage the authors of a book by the edit metadata dialog. You can manage individual authors by right clicking on the author in the Tag browser on the left of the main calibre window and selecting Manage authors. Using this dialog you can change the name of an author and also how that name is sorted. This will automatically change the name of the author in all the books of that author. When a book has multiple authors, separate their names using the & character.

Şimdi yazar ismi sıralamaya gelelim:

- Calibre’ye yeni bir yazar eklendiğinde (yeni yazara sahip bir kitap eklendiğinde olur bu), calibre hem kitap hem yazar için otomatik olarak bir karakter dizisi hesaplar.

- Authors in the Tag browser are sorted by the sort value for the authors. Remember that this is different from the Author sort field for a book.

- Varsayılan olarak, bu sıralama algoritması yazar isminin Ad Soyad biçiminde olduğunu varsayar ve bir Soyad, Ad sıralama değeri üretir.

- You can change this algorithm by going to Preferences → Advanced → Tweaks and setting the author_sort_copy_method tweak.

- You can force calibre to recalculate the author sort values for every author by right clicking on any author and selecting Manage authors, then pushing the Recalculate all author sort values button. Do this after you have set the author_sort_copy_method tweak to what you want.

- You can force calibre to recalculate the author sort values for all books by using the bulk metadata edit dialog (select all books and click edit metadata, check the Automatically set author sort checkbox, then press OK).

- Kitaplar için yazar sıralama değerlerini tekrar hesaplarken, calibre her yazar için tek tek yazar sıralama değerlerini kullanır. Böylece kitapların yazar sıralama değerlerini tekrar hesaplamadan önce her bir yazar sıralama değerlerinin düzgün olduğundan emin olur.

- You can control whether the Tag browser display authors using their names or their sort values by setting the categories_use_field_for_author_name tweak in Preferences → Advanced → Tweaks

Yazarları yönet kullanarak her bir yazarın sıralama değerini ayarlayabileceğinizi unutmayın. Bu, calibre’nin doğru algılayamayacağı isimlerle uğraşırken faydalıdır, örneğin Miguel de Cervantes Saavedra gibi karmaşık çok parçalı isimler veya Sun Tzu gibi Asya isimleri ile.

Tüm bu esneklikle, calibre yazar adlarını istediğiniz gibi yönetmenize imkan tanır. Örneğin, çok gelen bir istek yazarları Soyad, Ad şeklinde göstermektir. Bunu yapmak için, aşağıdaki not sizin için geçerli değilse:

- Yukarıda anlatıldığı gibi author_sort_copy_method ayarını kopyala olarak ayarlayın.

- Calibre’yi yeniden başlatın. Kalan adımları yapmadan herhangi bir kitap metadata değişikliği yapmayın.

- Yazarları yönet iletişim penceresini kullanarak tüm yazar adlarını Soyad, Ad şeklinde değiştirin.

- Tüm yazarları değiştirdikten sonra, Tüm yazar sıralama değerlerini yeniden hesapla düğmesine tıklayın.

- Tamama tıklayın, bu noktada calibre tüm kitaplarınızdaki yazarları değiştirir. Bu biraz sürebilir.

Not

Ad Soyaddan Soyad Ada değişiklik yaparken, author_sort içindeki değerlerin Soyad, Ad biçiminde olmasına genelde rastlanır. Bu durumdaysanız aşağıdakini yapın:

- Yukarıda anlatıldığı gibi author_sort_copy_method ayarını kopyala olarak ayarlayın.

- Calibre’yi yeniden başlatın. Kalan adımları yapmadan herhangi bir kitap metadata değişikliği yapmayın.

- Open the Manage authors dialog. Press the copy all author sort values to author button.

- Yazarlara göz atıp istediğinizin bu olduğundan emin olun. Hala Vazgeçe tıklayarak değişiklikleri iptal edebilirsiniz. Tamama tıkladığınızda, geri dönüşü yoktur.

- Tamama tıklayın, bu noktada calibre tüm kitaplarınızdaki yazarları değiştirir. Bu biraz sürebilir.

Why doesn’t calibre let me store books in my own folder structure?

The whole point of calibre’s library management features is that they provide a search and sort based interface for locating books that is much more efficient than any possible folder scheme you could come up with for your collection. Indeed, once you become comfortable using calibre’s interface to find, sort and browse your collection, you won’t ever feel the need to hunt through the files on your disk to find a book again. By managing books in its own folder structure of Author -> Title -> Book files, calibre is able to achieve a high level of reliability and standardization. To illustrate why a search/tagging based interface is superior to folders, consider the following. Suppose your book collection is nicely sorted into folders with the following scheme:

Genre -> Author -> Series -> ReadStatus

Bu, Foundation serisindeki Isaac Asimov tarafından yazılan tüm bilim kurgu kitaplarını bulmayı çok kolaylaştırır. Ama tüm okunmamış bilim kurgu kitaplarını bulmak istediğinizi düşünün. Bu dizin yapısıyla bunu yapmanın kolay bir yolu yoktur, yerine, şu şekilde görünen bir dizin yapısına ihtiyaç duyarsınız:

ReadStatus -> Genre -> Author -> Series

Calibre’de ise, türleri ve okunma durumunu işaretlemek için etiketler kullanır ve örneğin tag:scifi and not tag:read gibi basit bir arama sorgusu kullanırsınız. Hatta calibre’nin güzel bir grafik arayüzü olduğundan arama dilini öğrenmenize gerek kalmadan sadece etiketlere tıklayarak aramaya dahil eder ya da aramadan çıkarırsınız.

To those of you that claim that you need access to the filesystem, so that you can have access to your books over the network, calibre has an excellent Content server that gives you access to your calibre library over the net.

If you are worried that someday calibre will cease to be developed, leaving all your books marooned in its folder structure, explore the powerful Save to disk feature in calibre that lets you export all your files into a folder structure of arbitrary complexity based on their metadata.

Son olarak, her başlık dizinin sonunda sayı olmasının sebebi sağlamlıktır. Bu numara calibre veri tabanındaki kitap kaydının kimlik numarasıdır. Numaranın varlığı aynı başlık ve yazar ismine sahip birden fazla kaydınız olmasını sağlar. Ayrıca calibre’nin veritabanı dosyası bozulduğunda tüm metadata ile beraber veri tabanını sihirli bir şekilde yeniden oluşturmasını sağlayan yapının parçasıdır. Calibre’nin amacının metadata’yı dosya isimlerinde saklamayı ve bir şeyleri bulmak için dosya sistemini kullanmayı durdurmak olduğunu düşünürsek, kimlik numaralarının dizin isimlerinde çirkin durmasının artan sağlamlık yanında önemsiz olduğunu söyleyebiliriz.

Hala tatmin olmadıysanız, sanırım calibre size göre değil. Kitap kataloglama ihtiyaçlarınız için başka bir çözüm arayın. Şunu açıkça belirtelim, bu değişmeyecek. Bunu değiştirmemiz için lütfen bizimle iletişime geçmeyin.

Neden calibre’nin x için sütunu yok?

calibre is designed to have columns for the most frequently and widely used fields. In addition, you can add any columns you like. Columns can be added via Preferences → Interface → Add your own columns. Watch the tutorial UI Power tips [https://calibre-ebook.com/demo#tutorials] to learn how to create your own columns, or read this blog post [https://blog.calibre-ebook.com/calibre-custom-columns/].

Ayrıca diğer sütunlardan metadata kombinasyonları içeren “sanal sütunlar” da oluşturabilirsiniz. Sütun ekleme iletişim penceresinde Hızlı oluştur bağlantılarını kullanarak kolaylıkla kitap ISBN veya biçimlerini gösterebilirsiniz. Calibre’nin güçlü şablon dilini kullanarak sütunlarla çok daha fazlasını yapabilirsiniz. Daha fazla detay için, bknz Calibre şablon dili.

Biçimleri veya ISBN’i gönderek bir sütunum olabilir mi?

Evet. Özel sütunlar eklemek için aşağıdaki cevaptaki yönergeleri takip edin.

Calibre kitaplığı verimi bir bilgisayardan diğerine nasıl aktarabilirim?

You can export all calibre data (books, settings and plugins) and then import it on another computer. First let’s see how to export the data:

- Right click the calibre icon in the main calibre toolbar and select Export/import all calibre data. Note that if there is currently a device connected, this menu option will not be available – so, disconnect any connected devices. Then click the button labelled Export all your calibre data. You will see a list of all your calibre libraries. Click OK and choose an empty folder somewhere on your computer. The exported data will be saved in this folder. Simply copy this folder to your new computer and follow the instructions below to import the data.

- Install calibre on your new computer and run through the Welcome wizard, it does not matter what you do there, as you will be importing your old settings in the next step. You will now have an empty calibre, with just the Getting Started guide in your library. Once again, right click the calibre button and choose Export/import all calibre data. Then click the button labelled Import previously exported data. Select the folder with the exported data that you copied over earlier. You will now have a list of libraries you can import. Go through the list one by one, and select the new location for each library (a location is just an empty folder somewhere on your computer). Click OK. After the import completes, calibre will restart, with all your old libraries, settings and calibre plugins.

Not

This import/export functionality is only available from calibre version 2.47 onwards. If you have an older version of calibre, or if you encounter problems with the import/export, you can just copy over your calibre library folder manually, as described in the next paragraph.

Simply copy the calibre library folder from the old to the new computer. You can find out what the library folder is by clicking the calibre icon in the toolbar. Choose the Switch/create calibre library action and you will see the path to the current calibre library.

Now on the new computer, start calibre for the first time. It will run the Welcome wizard asking you for the location of the calibre library. Point it to the previously copied folder. If the computer you are transferring to already has a calibre installation, then the Welcome wizard won’t run. In that case, right-click the calibre icon in the toolbar and point it to the newly copied folder. You will now have two calibre libraries on your computer and you can switch between them by clicking the calibre icon on the toolbar. Transferring your library in this manner preserver all your metadata, tags, custom columns, etc.

Calibre’deki kitap listesi boş!

Bunun neden olduğunu anlamak için, calibre kitaplığının ne olduğunu anlamalısınız. En basit düzeyde, calibre kitaplığı sadece bir dizindir. Calibre’ye her kitap eklediğinizde, bu kitabın dosyaları bu dizine kopyalanır (alt dizinlerde yazar ve başlığa göre düzenlenerek). Calibre kitaplık dizininde, en üst seviyede, metadata.db adında bir dosya görürsünüz. Bu dosya calibre’nin tüm kitaplar için başlık/yazar/puan/etiketler vs. gibi metadata bilgisini tuttuğu dosyadır. Calibre’nin görüntülediği kitap listesi bu metadata.db dosyasının içeriğini okumasıyla oluşturulur.

Calibre’nin boş kitap listesi göstermesinin iki sebebi olabilir:

- Calibre kitaplık dizininiz yer değiştirdi. Bu, eğer harici diskinizdeyse ve sürücü harfi değiştiyse olabilir. Veya yanlışlıkla dizini taşıdıysanız. Bu durumda, calibre kitaplığı bulamadığından boş bir kitaplık ile açılır. Düzeltmek için, calibre araç çubuğundaki calibre simgesine sağ tıklayarak kitaplık değiştir/oluştur tıklayın. Calibre kitaplığınızın yeni konumunu küçük mavi simgeye tıklayarak seçin ve Tamama tıklayın. Yeni konumu bilmiyorsanız bilgisayarınızda metadata.db dosyasını aratın.

- Metadata.db dosyanız siliniş/bozuk olabilir. Bu durumda calibre’den metadata.db dosyasını yedeklerden tekrar oluşturmasını isteyebilirsiniz. Calibre araç çubuğundaki calibre simgesine sağ tıklayın ve Kitaplık bakımı->Veri tabanını geri yükleyi seçin. calibre metadata.db dosyasını tekrar oluşturacaktır.

Bir ağ sürücüsünde/NAS’da calibre kitaplığımla ilgili hatalar alıyorum?

Calibre kitaplığınızı bir ağ sürücüsüne koymayın.

Dosya sistemi karmaşık bir yapıdır. Çoğu ağ dosya sistemleri calibre’nin kullandığı çeşitli dosya sistemi özelliklerini desteklemez. Bazısı dosya kitlemeyi, bazısı linklemeyi desteklemez, bazıları da acayiptir. Ek olarak, calibre tek kullanıcılı bir uygulamadır, yanlışlıkla aynı ağa bağlı kitaplığın iki kopyasını çalıştırırsanız, kötü şeyler olur. Son olarak, farklı işletim sistemleri dosya sistemlerine farklı yaklaşırlar, yani ağdaki sürücünüzü farklı işletim sistemleriyle paylaşırsanız, kötü şeyler olacaktır.

Consider using the calibre Content server to make your books available on other computers. Run calibre on a single computer and access it via the Content server or a Remote Desktop solution.

Kitaplığı illa paylaşmanız gerekiyorsa, ağ sürücüsü yerine DropBox veya rsync gibi bir dosya eşzamanlama aracı kullanın. Bir dosya eşzamanlama aracı kullanıyorsanız hem calibre hem eş zamanlama aracının kitaplığa aynı zamanda erişmemesini sağlamak gereklidir. Başka bir deyişle, calibre ve eş zamanlama aracını aynı zamanda çalıştırmayın.

Even with these tools there is danger of data corruption/loss, so only do this if you are willing to live with that risk. In particular, be aware that Google Drive is incompatible with calibre, if you put your calibre library in Google Drive, you will suffer data loss. See this thread [https://www.mobileread.com/forums/showthread.php?t=205581] for details.

Muhtelif

İçindekiler

- Amazon is stopping email delivery of MOBI files?

- Calibre’nin sık kullanılan haber sitemden haberleri indirmesini istiyorum.

- Neden calibre ismi?

- Why does calibre show only some of my fonts on macOS?

- calibre Windows’da başlamıyor?

- calibre sık sık donuyor/çöküyor?

- The calibre E-book viewer and Edit book tools do not work on Windows?

- Görüntüleyiciyi kullanmak ya da herhangi bir dönüştürme yapmak Windows üzerinde izin verilmedi hatasına yol açıyor

- calibre is not starting/crashing on macOS?

- I get only a black or white screen when running the calibre E-book viewer?

- Yükleyiciyi indirdim ama çalışmıyor?

- Antivirüs programım calibre’nin bir virüs/trojen olduğunu söylüyor?

- Calibre’yi nasıl yedeklerim?

- Satın aldığım EPUB kitapları calibre ile nasıl kullanırım (veya .acsm dosyalarıyla ne yapacağım)?

- “İzin Verilmedi” hatası alıyorum?

- Yorum metadata’sının okuyucumda görüntülenmesini sağlayabilir miyim?

- Calibre’nin HTTP vekilimi kullanmasını nasıl sağlarım?

- Calibre’ye bir özelliğin eklenmesini istiyorum. Ne yapabilirim?

- Calibre’nin neden otomatik güncellemesi yok?

- Calibre’nin lisansı nedir?

- Calibre’yi USB diskimden nasıl çalıştırırım?

- How do I run parts of calibre like news download and the Content server on my own Linux server?

Amazon is stopping email delivery of MOBI files?

Amazon have announced [https://blog.the-ebook-reader.com/2022/05/03/amazon-dropping-mobi-support-on-send-to-kindle-apps/] that they will stop accepting MOBI files emailed to @kindle.com email addresses. You can instruct calibre to send EPUB instead of MOBI by going to Preferences → Sharing books by email and then removing MOBI from the list of formats to send to your @kindle.com email address and adding EPUB instead.

Note however, that Amazon’s EPUB intake is very flawed, they will reject a number of EPUB files that work everywhere else. In such cases you can try the following trick:

1. Convert the EPUB file to MOBI

2. Then convert the MOBI file back to EPUB and send the resulting EPUB file

This will remove all advanced formatting, embedded fonts, etc., but greatly increase the chances of Amazon accepting the EPUB.

Not

If you were previously using email delivery of periodicals downloaded by calibre, you will be better off sending those by USB cable or downloading them from the calibre Content server via the Kindle’s built-in browser. However, if you want to continue using email delivery you can try changing the output format in Preferences->Behavior to EPUB, then calibre will download the news in EPUB format. Whether Amazon will accept the EPUB or not is a whole other question.

Calibre’nin sık kullanılan haber sitemden haberleri indirmesini istiyorum.

Bilgisayarlarla aranız biraz iyiyse, calibre’ye seçtiğiniz herhangi bir siteden haberleri indirmesini öğretebilirsiniz. Bunu nasıl yapacağınızı öğrenmek için bknz Sık kullandığınız haber sitesini ekleme.

Otherwise, you can request a particular news site by posting in the calibre Recipes forum [https://www.mobileread.com/forums/forumdisplay.php?f=228].

Neden calibre ismi?

İstediğinizi seçin:

- Converter And LIBRary for E-books

- Yüksek calibre ürün

- A tribute to the SONY Librie which was the first e-ink based e-book reader

- Eşim seçti ;)

calibre cal-i-ber olarak söylenir ca-li-bre değil. Merak ediyorsanız, calibre caliber’in İngiliz/kamu söylenişidir. Bir Hintli olarak bana normal gelen söyleniş de bu.

Why does calibre show only some of my fonts on macOS?

calibre embeds fonts in e-book files it creates. E-book files support embedding only TrueType and OpenType (.ttf and .otf) fonts. Most fonts on macOS systems are in .dfont format, thus they cannot be embedded. calibre shows only TrueType and OpenType fonts found on your system. You can obtain many such fonts on the web. Simply download the .ttf/.otf files and add them to the Library/Fonts folder in your home folder.

calibre Windows’da başlamıyor?

Bunun bir takım sebepleri olabilir:

- If you get no errors but the calibre window does not appear, it has probably just appeared off screen. You can gather all windows onto the current screen using one of the techniques described here [https://www.wikihow.com/Bring-an-Off-Screen-Window-Back-on-Windows].

- Calibre’nin başka bir program tarafından açık olduğundan bir dosyayı açamadığıyla ilgili hata alıyorsanız, şunu yapın:

- Calibre’yi kaldırın

- Bilgisayarı yeniden başlatın

- Calibre’yi tekrar yükleyin. Ama yükleme sihirbazından calibre’yi başlatmayın.

- Geçici olarak anti virüs programınızı kapatın (emin olmak için yapmadan önce internet bağlantısını kesin)

- Calibre kitaplığınız için seçtiğiniz dizin içine bakın. metadata.db adında bir dosya görürseniz, silin.

- Calibre’yi başlatın

- Bu noktada calibre’yi normal şekilde başlatabiliyor olmalısınız.

- Calibre’yi güncelledikten sonra bir Python fonksiyonunun beklenmedik şekilde sonlandığıyla ilgili hata alırsanız, önce calibre’yi silin, sonra (eğer mevcutsa) C:\Program Files\Calibre ve C:\Program Files\Calibre2 altındaki dizinleri silin. Şimdi tekrar yükleme yaptığınızda düzelmeli.

- If you get an error in the Welcome wizard on an initial run of calibre, try choosing a folder like C:\library as the calibre library (calibre sometimes has trouble with library locations if the path contains non-English characters, or only numbers, etc.)

- Try running it as administrator (Right click on the icon and select Run as administrator)

If it still won’t launch, start a command prompt (press the Windows key and R; then type cmd.exe in the Run dialog that appears). At the command prompt type the following command and press Enter:

calibre-debug -g

Post any output you see in a help message on the Forum [https://www.mobileread.com/forums/forumdisplay.php?f=166].

calibre sık sık donuyor/çöküyor?

Buna sebep olabilecek bildiğim bir kaç sorun var:

- Bilgisayarınıza son zamanlarda harici bir TV veya ekran bağladınız. Bu durumda, calibre metadata düzenleme veya dönüştürme gibi herhangi bir iletişim penceresini açtığında pencere ikinci ekranda çıkar, bu da sizin calibre’ni donduğunu sanmanıza sebep olur. İkinci ekranı çıkarın ve calibre’yi yeniden başlatın.

- The following programs have been reported to cause crashes in calibre: If you are running any of these, close them before starting calibre, or uninstall them: RoboForm, Logitech SetPoint Settings, Constant Guard Protection by Xfinity, Spybot, Killer Network Manager, Nahimic UI Interface, Acronis True Image.

- You are using a Wacom branded USB mouse/tablet. There is an incompatibility between Wacom drivers and the graphics toolkit calibre uses. Try using a non-Wacom mouse.

- Bazı 64 bit Windows sürümlerinde 64-bit calibre’nin düzgün çalışmasını engelleyen bazı güvenlik yazılımları/ayarları vardır. Calibre’nin 64-bit sürümünü kullanıyorsanız 32-bit sürüme geçmeyi deneyin.

- If the crash happens when you are trying to copy text from the calibre E-book viewer, it is most likely caused by some clipboard monitoring/managing application you have running. Turn it off and you should be fine.

- If the crashes happen specifically when you are using a file dialog, like clicking on the Add books button or the Save to Disk button, then you have some software that has installed broken Shell extensions on your computer. Known culprits include: SpiderOak, odrive sync and Dell Backup and Recovery and NetDrive. If you have one of these, uninstall them and you will be fine. You can also use the NirSoft Shell Extension Viewer [https://www.nirsoft.net/utils/shexview.html] to see what shell extensions are installed on your system and disable them individually, if you don’t want to uninstall the full program. Remember to use “Restart Explorer” or reboot your computer after disabling the shell extensions.

Yukarıdakilerin hiçbiri işinizi görmüyorsa, bilgisayarınızda calibre ile çakışan başka bir program var demektir. Bilgisayarınızı güvenli kipte yeniden başlatın, böylece olabildiğince az programla başlayacaktır, ve çökmeler hala oluyor mu kontrol edin. Eğer olmuyorsa, sorunun bir programdan kaynaklandığını anlarsınız. En şüpheli programlar diğer programların davranışını değiştirenlerdir, örneğin antivirüs, aygıt sürücüsü, RoboForm gibi bir şey (otomatik form doldurma uygulaması) veya Ses Kontrol veya Ekran Okuyucuy gibi yardımcı bir teknoloji.

Sorun kaynağını bulmanın tek yolu programları tek tek eleyerek soruna yol açanı anlamaktır. Temel olarak, programı durdurun, calibre’yi çalıştırın, çökme var mı kontrol edin. Hala sorun varsa başka bir programı durdurun ve tekrar edin.

The calibre E-book viewer and Edit book tools do not work on Windows?

These two programs use hardware acceleration as they embed a version of the Chrome browser to render HTML. If they do not work it will be because of incompatibility with your system’s GPU (graphics) drivers. Try updating these first, and reboot. If that does not fix it, you can set the QTWEBENGINE_CHROMIUM_FLAGS environment variable to the value --disable-gpu to turn off hardware acceleration. See this page [https://doc.qt.io/qt-6/qtwebengine-debugging.html] for details.

Görüntüleyiciyi kullanmak ya da herhangi bir dönüştürme yapmak Windows üzerinde izin verilmedi hatasına yol açıyor

Something on your computer is preventing calibre from accessing its own temporary files. Most likely the permissions on your Temp folder are incorrect. Go to the folder file:C:\Users\USERNAME\AppData\Local in Windows Explorer and then right click on the file:Temp folder, select Properties and go to the Security tab. Make sure that your user account has full control for this folder.

Some users have reported that running the following command in an Administrator Command Prompt fixed their permissions. To get an Administrator Command Prompt search for cmd.exe in the start menu, then right click on the command prompt entry and select Run as administrator. At the command prompt type the following command and press Enter:

icacls "%appdata%\..\Local\Temp" /reset /T

Alternatif olarak, calibre’yi Yönetici olarak çalıştırabilirsiniz, ama böyle yapmak sürükle bırak gibi bazı işlevlerin çalışmamasına yol açar.

Son olarak, bazı kullanıcılar UAC’nin kapatılmasının problemi çözdüğünü raporladılar.

calibre is not starting/crashing on macOS?

One common cause of failures on macOS is the use of accessibility technologies that are incompatible with the graphics toolkit calibre uses. Try turning off VoiceOver if you have it on. Also go to System Preferences->System->Universal Access and turn off the setting for enabling access for assistive devices in all the tabs. Another cause can be some third party apps that modify system behavior, such as Smart Scroll.

Calibre’nin neden başlamadığıyla ilgili hata ayıklama çıktısını Console.app çalıştırarak alabilirsiniz. Hata çıktısı buraya yazılacaktır. Eğer hata çıktısı şuna benzer bir satır içeriyorsa:

Qt: internal: -108: Error ATSUMeasureTextImage text/qfontengine_mac.mm

then the problem is probably a corrupted font cache. You can clear the cache by following these instructions [https://www.macworld.com/article/1139383/fontcacheclear.html]. If that doesn’t solve it, look for a corrupted font file on your system, in ~/Library/Fonts or the like. An easy way to check for corrupted fonts in macOS is to start the “Font Book” application, select all fonts and then in the File menu, choose “Validate fonts”.

I get only a black or white screen when running the calibre E-book viewer?

This will be because of an incompatibility between Qt WebEngine, which the viewer uses to render and the GPU drivers on your system. First try upgrading the GPU drivers. If that does not help, you can try turning off hardware acceleration in Qt WebEngine by setting the environment variable QTWEBENGINE_CHROMIUM_FLAGS to the value --disable-gpu. See Çevresel değişkenler for how to change environment variables.

Yükleyiciyi indirdim ama çalışmıyor?

Downloading from the Internet can sometimes result in a corrupted download. If the calibre installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from an alternate location [https://github.com/kovidgoyal/calibre/releases/latest]. If the installer still doesn’t work, then something on your computer is preventing it from running.

- Geçici olarak antivirüs programınızı kapatmayı deneyin (Microsoft Security Essentials, veya Kaspersky veya Norton veya McAfee veya her neyse). Güncelleme süreci yarıda kalıyorsa sorun büyük ihtimalle budur.

- Similarly, if the installer is failing/rolling back and you have Microsoft PowerToys running, quit it.

- Try rebooting your computer and running a registry cleaner like Wise registry cleaner [https://www.wisecleaner.com].

- Temiz bir yükleme deneyin. Yani, calibre’yi silin, C:\Program Files\Calibre2 (veya calibre’yi nereye yüklemeyi seçtiyseniz) silin. Calibre’yi baştan kurun. Kaldırmanın kitaplarınızı veya ayarlarınızı silmeyeceğini hatırlatalım.

- Try downloading the installer with an alternate browser. For example if you are using Microsoft Edge, try using Firefox or Chrome instead.

- If you get an error about a missing DLL on Windows, then most likely, the permissions on your temporary folder are incorrect. Go to the folder C:\Users\USERNAME\AppData\Local in Windows Explorer and then right click on the Temp folder and select Properties and go to the Security tab. Make sure that your user account has full control for this folder.

If you still cannot get the installer to work and you are on Windows, you can use the calibre portable install [https://calibre-ebook.com/download_portable], which does not need an installer (it is just a ZIP file).

Antivirüs programım calibre’nin bir virüs/trojen olduğunu söylüyor?

The first thing to check is that you are downloading calibre from the official website [https://calibre-ebook.com/download]. Make sure you are clicking the download links on the left, not the advertisements on the right. calibre is a very popular program and unscrupulous people try to setup websites offering it for download to fool the unwary.

Resmi siteden indirdiyseniz ve antivirüs programınız hala calibre’yi virüs olarak görüyorsa, antivirüs programınız yanılıyor demektir. Antivirüs programları sezgisel davranırlar, “şüpheli görünen” kod parçaları ararlar. Irka göre ayırma gibi bir şeydir bu. calibre tamamen açık kaynaklı bir yazılımdır. Bir virüs programı olmadığını görmek için tüm kaynak kodunu kendiniz tarayabilirsiniz (veya bu işi yapması için birini kiralayabilirsiniz). Lütfen yanlış tanıyı hangi firmadan antivirüs programı alıyorsanız oraya bildirin. Antivirüs programı calibre’yi indirmenizi/yüklemenizi engelliyorsa, geçici olarak kapatın, calibre’yi yükleyin ve tekrar açın.

Calibre’yi nasıl yedeklerim?

Yedeklenecek en önemli şey calibre kitaplık dizinidir, burası tüm kitaplarınızı ve metadata’yı barındırır. Calibre’yi ilk defa çalıştırdığınızda size sorulan calibre kitaplığı dizini burasıdır. Ana araç çubuğundaki calibre simgesine tıklayarak kitaplık dizininin yolunu alabilirsiniz. Bu dizini tamamen tüm alt dizinleriyle yedeklemeniz gerekir.

Araç çubuğundaki calibre simgesine tıklayıp daha önceden yedek alınmış bir kitaplık dizinini kolayca kullanmaya başlayabilirsiniz. Yedeklenmiş bir kitaplık dizini özel sütunlarınızı ve kaydedilmiş aramalarınızı da kitap ve metadata bilgisiyle beraber tutar.

If you want to backup the calibre configuration/plugins, you have to backup the config folder. You can find this config folder via Preferences → Miscellaneous. Note that restoring configuration folders is not officially supported, but should work in most cases. Just copy the contents of the backup folder into the current configuration folder to restore.

Satın aldığım EPUB kitapları calibre ile nasıl kullanırım (veya .acsm dosyalarıyla ne yapacağım)?

Most purchased EPUB books have DRM. This prevents calibre from opening them. You can still use calibre to store and transfer them to your e-book reader. First, you must authorize your reader on a Windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with calibre will work fine on your reader. When you purchase an epub book from a website, you will get an “.acsm” file. This file should be opened with Adobe Digital Editions, which will then download the actual “.epub” e-book. The e-book file will be stored in the folder “My Digital Editions”, from where you can add it to calibre.

“İzin Verilmedi” hatası alıyorum?

İzin verilmedi hatası bir takım sebeplerden olabilir, hiçbirinin calibre ile bir ilgisi yoktur.

- Yazma koruması açık bir SD kart kullanıyorsanız izin verilmedi hatası alabilirsiniz.

- On macOS if you get permission errors when connecting a device to calibre, you can fix that by looking under System Preferences > Security and Privacy > Privacy > Files and Folders.

- Siz ya da kullandığınız herhangi bir program dosyaların izinlerini yalnızca okunur olarak değiştirdiyse alabilirsiniz.

- Aygıtta bir dosya sistemi hatası olduysa bu işletim sisteminizin dosya sistemini yalnızca okunur kipte bağlamasına yol açar veya belli bir dosyayı kurtarılmayı bekliyor olarak yalnızca okunur şekilde işaretler.

- Dosyaların sahibi sizden başka biri olarak ayarlıysa.

- Dosyanız başka bir program tarafından açılmışsa.

- If the file resides on a device, you may have reached the limit of a maximum of 256 files in the root of the device. In this case you need to reformat the device/sd card referred to in the error message with a FAT32 filesystem, or delete some files from the SD card/device memory.

Calibre kullanmaya devam etmeden önce izin hatalarının sebebini bulup düzeltmeniz gerekir. Hata iletisini dikkatlice okuyun, hangi dosyayı işaret ettiğine bakın ve bu dosyayı ya da dosyayı içeren dizini düzeltin.

Yorum metadata’sının okuyucumda görüntülenmesini sağlayabilir miyim?

Most readers do not support this. You should complain to the manufacturer about it and hopefully if enough people complain, things will change. In the meantime, you can insert the metadata, including comments into a “Jacket page” at the start of the e-book, by using the option to “Insert metadata as page at start of book” during conversion. The option is found in the Structure detection section of the conversion settings. Note that for this to have effect you have to convert the book. If your book is already in a format that does not need conversion, you can convert from that format to the same format.

Another alternative is to create a catalog in e-book form containing a listing of all the books in your calibre library, with their metadata. Click-and-hold the Convert button to access the catalog creation tool. And before you ask, no you cannot have the catalog “link directly to” books on your reader.

Calibre’nin HTTP vekilimi kullanmasını nasıl sağlarım?

By default, calibre uses whatever proxy settings are set in your OS. Sometimes these are incorrect, for example, on Windows if you don’t use Microsoft Edge then the proxy settings may not be up to date. You can tell calibre to use a particular proxy server by setting the http_proxy and https_proxy environment variables. The format of the variable is: http://username:password@servername you should ask your network administrator to give you the correct value for this variable. Note that calibre only supports HTTP proxies not SOCKS proxies. You can see the current proxies used by calibre in Preferences->Miscellaneous.

Calibre’ye bir özelliğin eklenmesini istiyorum. Ne yapabilirim?

İki seçeneğiniz var:

1. Create a patch by hacking on calibre and send it to me for review and inclusion. See Development [https://calibre-ebook.com/get-involved].

2. Open a bug requesting the feature [https://calibre-ebook.com/bugs]. Remember that while you may think your feature request is extremely important/essential, calibre developers might not agree. Fortunately, calibre is open source, which means you always have the option of implementing your feature yourself, or hiring someone to do it for you. Furthermore, calibre has a comprehensive plugin architecture, so you might be able to develop your feature as a plugin, see Calibre’nin işlevselliğini artırmak için kendi eklentilerinizi yazmak.

Calibre’nin neden otomatik güncellemesi yok?

Bir çok sebepten:

- Her hafta güncellemeye gerek yok. Calibre’nin çalışmasından memnunsanız güncelleme iletilerini kapatın ve yolunuza devam edin. Senede bir gelip güncelleme var mı diye bakın. Güncelleme iletisi üzerinde güncelleme iletilerini kapatmak için bir kontrol kutusu bulunmaktadır.

- calibre downloads currently use about 150TB of bandwidth a month [https://calibre-ebook.com/dynamic/downloads]. Implementing automatic updates would greatly increase that and end up costing thousands of dollars a month, which someone has to pay.

- Güncellemeyi indiren ve çalıştıran bir iletişim penceresi uyarlarsam, en çok güncelleme yapan insanı şimdi çalışan websitesine gidip indirme mantığından haftada en fazla beş tık kar ettirebilirim. Calibre geliştirmesinde bundan çok daha yüksek öncelikli işler var.

- Calibre’yi indirmekten gerçekten bu kadar nefret ediyorsanız, ve sürekli güncel olmak istiyorsanız, kaynak koddan çalıştırmanızı öneririm, bu yöntem güncellemeyi çok kolaylaştırır. Yönergeler burada.

- There are third party automatic updaters for calibre made by calibre users in the calibre forum [https://www.mobileread.com/forums/forumdisplay.php?f=238].

Calibre’nin lisansı nedir?

calibre is licensed under the GNU General Public License v3 (an open source license). This means that you are free to redistribute calibre as long as you make the source code available. So if you want to put calibre on a CD with your product, you must also put the calibre source code on the CD. The source code is available for download [https://download.calibre-ebook.com]. You are free to use the results of conversions from calibre however you want. You cannot use either code or libraries from calibre in your software without making your software open source. For details, see The GNU GPL v3 [https://www.gnu.org/licenses/gpl.html].

Calibre’yi USB diskimden nasıl çalıştırırım?

A portable version of calibre is available here [https://calibre-ebook.com/download_portable].

How do I run parts of calibre like news download and the Content server on my own Linux server?

First, you must install calibre onto your Linux server. If your server is using a modern Linux distribution, you should have no problems installing calibre onto it.

Not
calibre needs GLIBC >= 2.31 and libstdc++ >= 6.0.28. If you have an older server, you will either need to compile these from source, or use calibre 3.48 which requires GLIBC >= 2.17 or 2.85.1 which requires GLIBC >= 2.13 or calibre 1.48 which requires only GLIBC >= 2.10. In addition, although the calibre command line utilities do not need a running X server, some of them do require the X server libraries to be installed on your system. This is because of Qt, which is used for various image processing tasks, and links against these libraries. If you get an ImportError about some Qt modules, you are likely missing some X libraries. Typical candidates are: libxcb-cursor0, libxcb-xinerama0, libegl1, libopengl0.

Calibre sunucuyu şu komutla çalıştırabilirsiniz:

/opt/calibre/calibre-server /path/to/the/library/you/want/to/share

You can download news and convert it into an e-book with the command:

/opt/calibre/ebook-convert "Title of news source.recipe" outputfile.epub

If you want to generate MOBI, use outputfile.mobi instead and use --output-profile kindle.

İndirilen haberleri şu komutla e-posta gönderebilirsiniz:

/opt/calibre/calibre-smtp

Tam komut satırını tahmin etmeyi okuyucuya bırakıyorum.

Son olarak, indirilen haberleri calibre kitaplığına şu komutla ekleyebilirsiniz:

/opt/calibre/calibredb add --with-library /path/to/library outfile.epub

Remember to read the Komut Satırı Arayüzü section of the calibre User Manual to learn more about these, and other commands.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler

Rehberler

Burada Calibre’nin Xpath ve Şablonlar gibi gelişmiş özelliklerini kullanmaya başlayacağınız rehberleri bulacaksınız.

- Sık kullandığınız haber sitesini ekleme
- Kitap alt gruplarını yönetim, örneğin “tür”
- XPath Rehberi
- Calibre şablon dili

- Calibre’de kurallı ifadelerin kullanımına dair her şey

- Calibre’nin işlevselliğini artırmak için kendi eklentilerinizi yazmak

- E-kitaplarda matematik dizgisi
- AZW3 • EPUB • MOBI katalogları oluşturma
- Sanal kitaplıklar

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »
- Kitap alt gruplarını yönetim, örneğin “tür”

Kitap alt gruplarını yönetim, örneğin “tür”

Bazı insanlar kitaplıklarındaki kitapları alt dizinlere benzer şekilde alt gruplara düzenlemek isterler. En çok verilen yanıt tür sıralı dizisi oluşturmak istemeleridir, ama başka bir sürü sebep de olabilir. Bir kullanıcı ders kitaplarını konu ve ders numarasına göre düzenlemenin yolunu sordu. Bir başkası hediyeleri konu ve alıcıya göre saklamak istedi. Bu klavuz bu gönderinin devamında tür örneği üzerinden devam edecek.

Before going on, please note that we are not talking about folders on the hard disk. Subgroups are not file folders. Books will not be copied anywhere. calibre’s library file structure is not affected. Instead, we are presenting a way to organize and display subgroups of books within a calibre library.

- Kurulum

- Aranıyor

- Kısıtlamalar

- Useful template functions

Tür gibi alt gruplar için yaygın olarak sağlanan gereksinimler:

- Bir alt grup (örn., tür) kitap içermelidir (göstermelidir), kitap kategorileri değil. Alt grupları calibre kullanıcı kategorilerinden ayıran budur.

- Bir kitap birden çok alt grup içinde olabilir (tür). Bu alt grupları fiziksel dosya dizinlerden ayırır.

- Al gruplar (türler) bir sıralı dizi oluşturmalıdır; alt gruplar başka alt gruplar içerebilir.

Tags give you the first two. If you tag a book with the genre then you can use the Tag browser (or search) to find the books with that genre, giving you the first. Many books can have the same tag(s), giving you the second. The problem is that tags don’t satisfy the third requirement. They don’t provide a hierarchy.

The calibre hierarchy feature gives you the third – the ability to see the genres in a ‘tree’ and the ability to easily search for books in genre or sub-genre. For example, assume that your genre structure is similar to the following:

Genre
. History
.. Japanese
.. Military
.. Roman
. Mysteries
.. English
.. Vampire
. Science Fiction
.. Alternate History
.. Military
.. Space Opera
. Thrillers
.. Crime
.. Horror
etc.

By using the hierarchy feature, you can see these genres in the Tag browser in tree form, as shown in the screen image. In this example the outermost level (Genre) is a custom column that contains the genres. Genres containing sub-genres appear with a small triangle next to them. Clicking on that triangle will open the item and show the sub-genres, as you can see with History and Science Fiction.

Bir türe tıklamak o türdeki ya da onun alt türlerindeki tüm kitapları arar. Örneğin, Bilim Kurguya tıklamak tüm üç alt türü de verebilir, Alternatif Tarih, Askeri, Uzay Operası. Alternatif Tarih’e tıklamak bu türdeki kitapları verir, Askeri ve Uzay Operası türlerini göz ardı eder. Tabi ki, bir kitabın birden çok türü olabilir. Bir kitabın hem Uzak Operası, hem Askeri türü varsa, her iki türe de tıkladığınızda bu kitabı görürsünüz. Arama aşağıda daha detaylı tartışılmıştır.

Resimden görebileceğinz bir başka şey de Askeri türün iki kere belirmesidir, bir kere Tarih altında ve bir kere Bilim Kurgu altında. Türler sıralı dizi şeklinde oldukları için, bunlar iki ayrı türdür. Kitap birinde olabilir, diğerinde olabilir, veya (şüpheli şekilde) ikisinde de. Örneğin, Winston Churchill’in “İkinci Dünya Savaşı” kitabı “Tarih.Askeri” içinde olabilir. David Weber’in Honor Harrington kitapları “Bilim Kurgu.Askeri” içinde, ve bu durum için ayrıca “Bilim Kurgu.Uzay Operası” içinde olabilir.

Bir tür mevcut olduğunda, ki bu en az bir kitabın bu türde olması demektir, bu türü diğer kitaplara kitaplık görünümünden kitapları istediğiniz tür üzerine sürükleyerek kolaylıkla uygulayabilirsiniz. Türleri ayrıca metadata düzenleyicilerde de uygulayabilirsiniz; bu konuda daha fazlası aşağıda.

Kurulum

By now, your question might be “How was all of this setup?” There are three steps: 1) create the custom column, 2) tell calibre that the new column is to be treated as a hierarchy, and 3) add genres.

You create the custom column in the usual way, using Preferences -> Add your own columns. This example uses “#genre” as the lookup name and “Genre” as the column heading. The column type is “Comma-separated text, like tags, shown in the Tag browser.”

Then after restarting calibre, you must tell calibre that the column is to be treated as a hierarchy. Go to Preferences  →  Look & feel  →  Tag browser and enter the lookup name “#genre” into the “Categories with hierarchical items” box. Press Apply, and you are done with setting up.

Bu noktada sütunda hiçbir tür yoktur. Son adıma geldik: bir kitaba tür uygulama. Bir kitap türü en az bir kitapta bulunana kadar calibre’de var olmaz. İlk defa bir türün nasıl uygulanacağını öğrenmek için, bir türün kitap metadata’sında nasıl göründüğünden biraz bahsetmeliyiz.

Bir “şeylerin” sıralı dizisi noktalarla ayrılmış ifadelerden oluşan öğeler oluşturmakla inşa edilir. Tür örneğinden devam edersek, bu öğeler “Tarih.Askeri”, “Gizem.Vampir”, “Bilim Kurgu.Uzay Operası”, vs. olur. Yani yeni bir tür oluşturmak için, bu türe ait olması gereken bir kitap seçer, metadata’sını düzenler, ve oluşturduğunuz sütuna yeni türü girersiniz. Örnekten devam edersek, bir kitaba yeni bir tür olan ve “Süper kahramanlar” alt türlü “Çizgiroman” türünü atamak isterseniz, bu (çizgiroman) kitabın ‘metadatasını düzenler’, Özel metadata sekmesini seçer, ve aşağıda gösterildiği gibi “Çizgiroman.Süper Kahramanlar” girersiniz (diğer özel sütunları göz ardı edin):

After doing the above, you see in the Tag browser:

Burdan sonra, bu yeni türü bir kitaba (çizgiroman, muhtemelen) atamak için, kitabı tür üzerine sürükleyebilir, ya da yukarıda yapıldığı gibi aynı şekilde metadata düzenleyerek kitaba ekleyebilirsiniz.

Not

Hierarchical display only works if the Tag browser is set to sort items by name. This is the default and can be checked by clicking the Configure button at the bottom of the Tag browser.

Aranıyor

The easiest way to search for genres is using the Tag browser, clicking on the genre you wish to see. Clicking on a genre with children will show you books with that genre and all child genres. However, this might bring up a question. Just because a genre has children doesn’t mean that it isn’t a genre in its own right. For example, a book can have the genre “History” but not “History.Military”. How do you search for books with only “History”?

The Tag browser search mechanism knows if an item has children. If it does, clicking on the item cycles through 5 searches instead of the normal three. The first is the normal green plus, which shows you books with that genre only (e.g., History). The second is a doubled plus (shown above), which shows you books with that genre and all sub-genres (e.g., History and History.Military). The third is the normal red minus, which shows you books without that exact genre. The fourth is a doubled minus, which shows you books without that genre or sub-genres. The fifth is back to the beginning, no mark, meaning no search.

Kısıtlamalar

If you search for a genre then create a saved search for it, you can use the ‘restrict to’ box to create a Virtual library of books with that genre. This is useful if you want to do other searches within the genre or to manage/update metadata for books in the genre. Continuing our example, you can create a Saved search named ‘History.Japanese’ by first clicking on the genre Japanese in the Tag browser to get a search into the search field, entering History.Japanese into the saved search field, then pushing the “Save search” button (the green box with the white plus, on the right-hand side).

Kaydedilmiş aramayı oluşturduktan sonra, bunu bir kısıtlama olarak kullanabilirsiniz.

Useful template functions

Tür bilgisini diske kaydet veya aygıta göndermek gibi bir şablonda kullanmak isteyebilirsiniz. O zaman soru “En dıştaki tür isim veya isimlerini nasıl alırım?” olur. Bir calibre şablon fonksiyonu, alt öğeler, bu işi daha kolay yapmak için sağlanmıştır.

Örneğin, “Tarih/The Gathering Storm - Churchill, Winston” gibi, tür dizinleri yapmak için en dıştaki tür seviyesini diske-kaydet şablonuna eklemek istediğinizi farz edin. Bunu yapmak için sıralı dizinin ilk seviyesini çıkarmalı ve bir dizin yapmasını belirtmek için yatık çizgi ile beraber ön tarafa eklemelisiniz. Aşağıdaki şablon bu işi görür:

{#genre:subitems(0,1)||/}{title} - {authors}

Şablonlar ve subitems() fonksiyonu ile ilgili daha fazla bilgi için Şablon diline göz atın.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »
- XPath Rehberi

XPath Rehberi

Bu eğitimde, ‘HTML <https:/’nin [https:/'nin] isteğe bağlı bölümlerini seçmek için kullanılabilecek bir sorgulama dili olan ‘XPath <https://en.wikipedia.org/wiki/XPath>’_ hakkında hafif bir giriş yapılacaktır. /en.wikipedia.org/wiki/HTML>`_ calibre’deki belgeler. XPath yaygın olarak kullanılan bir standarttır ve Google’da araştırıldığında tonlarca bilgi elde edilir. Ancak bu eğitim, yapılandırılmamış bir HTML belgesinde bölüm başlıklarını bulmak gibi e-kitapla ilgili görevler için XPath kullanımına odaklanmaktadır.

İçindekiler

- Etiket ismine göre seçim

- Özniteliğe göre seçmek

- Etiket içeriğine göre seçmek

- Örnek e-kitap

- XPath tümleşik fonksiyonları

Etiket ismine göre seçim

Seçimin en basit formu etiketleri isme göre seçmektir. Örneğin; belgedeki tüm <h2> etiketlerini seçmek istediğinizi düşünelim. XPath sorgusu basitçe şöyle olacaktır:

//h:h2 (Selects all <h2> tags)

Baştaki // öneki belgedeki tüm düzeylerde ara anlamına gelmektedir. Şimdi ise <a> etiketi içindeki ``<span>``etiketlerini arıyor olalım. Bunu yapan sorgu:

//h:a/h:span (Selects <span> tags inside <a> tags)

Eğer belgedeki belirli bir düzeydeki etiketleri arayacak olursanız öneki değiştirin:

/h:body/h:div/h:p (Selects <p> tags that are children of <div> tags that are
children of the <body> tag)

Bu yalnızca Örnek e-kitap içindeki <p>XPath kullanımını göstermek için çok kısa bir e-kitap.</p> ile eşleşir ancak diğer <p> etiketlerinden hiçbiriyle eşleşmez. Yukarıdaki örneklerde h: öneki XHTML etiketleriyle eşleşmesi için gereklidir. Bunun nedeni dahili olarak calibre’nin tüm içeriği XHTML olarak temsil etmesidir. XHTML’de etiketlerin bir ad alanı vardır ve h: HTML etiketleri için ad alanı önekidir.

Şimdi <h1> ve <h2> etiketlerini seçmek isteyelim. Bunu yapabilmek için XPath’in predicate isimli yapısına ihtiyacımız vardır. Bir predicate basitçe etiketleri seçmek için bir testtir. Testler keyfi güçlü olabilir ve bu rehber ilerledikçe, daha güçlü örneklerini göreceksiniz. Bir predicate, test ifadesinin kare parantezler içine alınmasıyla oluşturulur:

//*[name()='h1' or name()='h2']

Bu XPath ifadesinde pek çok yenilik bulunmaktadır. Bunların ilki * karakteridir. herhangi bir etiketle eşleş anlamına gelmektedir. Şimdi test ifadesine bakalım name()='h1' or name()='h2'. name() tümleşik fonksiyona bir örnektir. Basitçe etiketin adını değerlendirir. Bu nedenle onu kullanarak, isimleri h1 veya h2 olan etiketleri seçebiliriz. name() fonksiyonunun isim alanlarını yok saydığını ve bu nedenle h: ön ekine ihtiyaç olmadığına dikkat edin. XPath çok fazla tümleşik fonksiyona sahiptir. Bunlardan bir kaçına bu rehberde değinilecektir.

Özniteliğe göre seçmek

Etiketleri özniteliklerine göre seçmek için bu ifadelerin kullanılması gereklidir:

//*[@style] (Select all tags that have a style attribute)
//*[@class="chapter"] (Select all tags that have class="chapter")
//h:h1[@class="bookTitle"] (Select all h1 tags that have class="bookTitle")

Buradaki @ operatörü etiketin özniteliğine karşılık gelir. Öznitelik değerleriyle ilgili daha karmaşık eşleşmeler için XPath tümleşik fonksiyonlarını kullanabilirsiniz.

Etiket içeriğine göre seçmek

XPath kullanarak etiketleri içerdikleri metinlere göre seçebilirsiniz. Bunu yapmanın en iyi yolu re:test() tümleşik fonksiyonunun düzenli ifadelerini kullanmaktır:

//h:h2[re:test(., 'chapter|section', 'i')] (Selects <h2> tags that contain the words chapter or
section)

Buradaki . operatörü etiketin içeriğine karşılık gelir, @ operatörünün özniteliklere karşılık gelmesi gibidir.

Örnek e-kitap

<html>
<head>
<title>A very short e-book</title>
<meta name="charset" value="utf-8" />
</head>
<body>
<h1 class="bookTitle">A very short e-book</h1>
<p style="text-align:right">Written by Kovid Goyal</p>
<div class="introduction">
<p>A very short e-book to demonstrate the use of XPath.</p>
</div>

<h2 class="chapter">Chapter One</h2>
<p>This is a truly fascinating chapter.</p>

<h2 class="chapter">Chapter Two</h2>
<p>A worthy continuation of a fine tradition.</p>
</body>
</html>

XPath tümleşik fonksiyonları

name()

Mevcut etiketin adı.

contains()

contains(s1, s2) ifadesi eğer s1, s2’yi içerirse true döndürür.

re:test()

re:test(src, model, flags), src dizesi pattern normal ifadesiyle eşleşiyorsa `true’ değerini döndürür. Özellikle kullanışlı bir bayrak ``i``dir, eşleştirmeyi büyük/küçük harfe duyarsız hale getirir. Düzenli ifadelerin sözdizimi hakkında iyi bir başlangıç noktası, ‘regexp sözdizimi <https://docs.python.org/library/re.html>’_ adresinde bulunabilir.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »
- Calibre şablon dili

Calibre şablon dili

The calibre template language is a calibre-specific language used throughout calibre for tasks such as specifying file paths, formatting values, and computing the value for user-specified columns. Examples:

- Specify the folder structure and file names when saving files from the calibre library to the disk or e-book reader.

- Define rules for adding icons and colors to the calibre book list.

- Define virtual columns that contain data from other columns.

- Advanced library searching.

- Advanced metadata search and replace.

The language is built around the notion of a template, which specifies which book metadata to use, computations on that metadata, and how it is to be formatted.

Basic templates

A basic template consists one or more template expressions. A template expression consists of text and names in curly brackets ({}) that is replaced by the corresponding metadata from the book being processed. For example, the default template in calibre used for saving books to device has 4 template expressions:

{author_sort}/{title}/{title} - {authors}

For the book “The Foundation” by “Isaac Asimov” the will become:

Asimov, Isaac/The Foundation/The Foundation - Isaac Asimov

The slashes are not template expressions because they are in between in {}. Such text is left where it appears. For example, if the template is:

{author_sort} Some Important Text {title}/{title} - {authors}

then for “The Foundation” the template produces:

Asimov, Isaac Some Important Text The Foundation/The Foundation - Isaac Asimov

A template expression can access all the metadata available in calibre, including custom columns (columns you create yourself), by using a column’s lookup name. To find the lookup name for a column (sometimes called fields), hover your mouse over the column header in calibre’s book list. Lookup names for custom columns always begin with #. For series type columns there is an additional field named #lookup name_index that is the series index for that book in the series. For example, if you have a custom series column named #myseries, there will also be a column named #myseries_index. The standard series column’s index is named series_index.

In addition to the standard column based fields, you also can use:

- {formats} - A list of formats available in the calibre library for a book

- {identifiers:select(isbn)} - The ISBN of the book

If the metadata for the field for a given book is not defined then the field in the template is replaced by the empty string (''). For example, consider the following template:

{author_sort}/{series}/{title} {series_index}

If Asimov’s book “Second Foundation” is in the series “Foundation” then the template produces:

Asimov, Isaac/Foundation/Second Foundation 3

If a series has not been entered for the book then the template produces:

Asimov, Isaac/Second Foundation

The template processor automatically removes multiple slashes and leading or trailing spaces.

Gelişmiş biçimleme

In addition to metadata substitution, templates can conditionally include additional text and control how substituted data is formatted.

Conditionally including text

Sometimes you want text to appear in the output only if a field is not empty. A common case is series and series_index where you want either nothing or the two values separated by a hyphen. calibre handles this case using a special template expression syntax.

For example and using the above Foundation example, assume you want the template to produce Foundation - 3 - Second Foundation. This template produces that output:

{series} - {series_index} - {title}

However, if a book has no series the template will produce - - the title, which is probably not what you want. Generally, people want the result be the title without the extraneous hyphens. You can accomplish this using the following template syntax:

{field:|prefix_text|suffix_text}

This template expression says that if field has the value XXXX then the result will be prefix_textXXXXXsuffix_text. If field is empty (has no value) then the result will be the empty string (nothing) because the prefix and suffix are ignored. The prefix and suffix can contain blanks.

Do not use subtemplates (`{ … }`) or functions (see below) in the prefix or the suffix.

Using this syntax, we can solve the above no-series problem with the template:

{series}{series_index:| - | - }{title}

The hyphens will be included only if the book has a series index, which it has only if it has a series. Continuing the Foundation example again, the template will produce Foundation - 1 - Second Foundation.

Notes:

- You must include the colon after the lookup name if you are using a prefix or a suffix.

- You must either use either no or both | characters. Using one, as in {field:| - }, is not allowed.

- It is OK to provide no text for either the prefix or the suffix, such as in {series:|| - }. The template {title:||} is the same as {title}.

Formatting

Suppose you want the series_index to be formatted as three digits with leading zeros. This does the trick:

{series_index:0>3s} - Three digits with leading zeros

For trailing zeros, use:

{series_index:0<3s} - Three digits with trailing zeros

If you use series indices with fractional values, e.g., 1.1, you might want the decimal points to line up. For example, you might want the indices 1 and 2.5 to appear as 01.00 and 02.50 so that they will sort correctly on a device that does lexical sorting. To do this, use:

{series_index:0>5.2f} - Five characters consisting of two digits with leading zeros, a decimal point, then 2 digits after the decimal point.

If you want only the first two letters of the data, use:

{author_sort:.2} - Only the first two letters of the author sort name

Much of the calibre template language formatting comes from Python. For more details on the syntax of these advanced formatting operations see the Python documentation [https://docs.python.org/3/library/string.html#formatstrings].

Using templates to define custom columns

Templates can be used to display information that isn’t in calibre metadata, or to display metadata differently from calibre’s normal format. For example, you might want to show the ISBN, a field that calibre does not display. You can accomplish this creating a custom column with the type Column built from other columns (hereafter called composite columns) and providing a template to generate the displayed text. The column will display the result of evaluating the template. For example, to display the ISBN, create the column and enter {identifiers:select(isbn)} in the template box. To display a column containing the values of two series custom columns, separated by a comma, use {#series1:||,}{#series2}.

Birleşik sütunlar, biçimleme dahil tüm şablon seçeneklerini kullanabilirler.

Note: You cannot edit the data displayed in a composite column. Instead you edit the source columns. If you edit a composite column, for example by double-clicking it, calibre will open the template for editing, not the underlying data.

Templates and plugboards

Santraller kitaba yazılan metadata’yı diske kaydetme ve aygıta gönderme işlemlerinde değiştirmeye yarar. Bir santral size kitabın metadata’sına yazılacak veriyi sağlayan şablonu belirtme izni verir. Santraller kullanarak şu alanları değiştirebilirsiniz: authors, author_sort, language, publisher, tags, title, title_sort. Bu özellik sıralama ya da görüntüleme sorunlarını aygıttaki kitapta değişik metadata kullanarak çözmek isteyen insanlara yardımcı olur.

When you create a plugboard, you specify the format and device for which the plugboard is to be used. A special device is provided, save_to_disk, that is used when saving formats (as opposed to sending them to a device). Once you have chosen the format and device, you choose the metadata fields to change, providing templates to supply the new values. These templates are connected to their destination fields, hence the name plugboards. You can of course use composite columns in these templates.

Plugboards are quite flexible and can be written in Single Function Mode, Template Program Mode, General Program Mode, or Python Template mode.

When a plugboard might apply (Content server, save to disk, or send to device), calibre searches the defined plugboards to choose the correct one for the given format and device. For example, to find the appropriate plugboard for an EPUB book being sent to an ANDROID device, calibre searches the plugboards using the following search order:

- biçim ve aygıtla birebir eşleşen bir santral, örn., EPUB ve ANDROID

- biçim ve özel any device seçimi ile birebir eşleşen bir santral, örn., EPUB ve any device

- özel any format seçimi ve aygıtla birebir eşleşmeli bir santral, örn., any format ve ANDROID

- any format ve any device olan bir santral

Etiket ve yazar alanları özel işlem gerektirir, çünkü her iki alan da birden fazla öğe tutabilir. Bir kitabın birden çok etiketi ve yazarı olabilir. Bu iki alandan birinin değişeceğini belirttiğinizde, şablonun sonucu birden fazla öğenin orada olup olmadığı hususunda incelenir. Etiketler için, sonuç, calibre’nin virgül bulduğu yerlerden kesilerek ayrılır. Örneğin, şablon Thriller, Horror değerini üretiyorsa, sonuç iki etiket olur Thriller ve Horror. Etiketin ortasına virgül koymanın bir yolu yoktur.

Aynı şey yazarlar için de geçerlidir, ama ayırma için virgül yerine & (ampersand) karakteri kullanılır. Örneğin, şablon Blogs, Joe&Posts, Susan değerini üretiyorsa, kitap iki yazarı var olarak sonlanacaktır, Blogs, Joe ve Posts, Susan. Şablon Blogs, Joe;Posts, Susan üretiyorsa, kitabın biraz garip isimli tek bir yazarı olacaktır.

Santraller diske kaydedilirken ya da aygıta yazılırken kitaba yazılan metadata’yı etkilerler. Santraller diske kaydet ve aygıta gönder tarafından dosya isimlerini oluşturmakta kullanılan metadata’yı etkilemezler. Bunun yerine, dosya isimleri, uygun seçenekler penceresinde girilen şablonlar kullanılarak oluştururlur.

Using functions in templates - Single Function Mode

Suppose you want to display the value of a field in upper case when that field is normally in title case. You can do this using template functions. For example, to display the title in upper case use the uppercase function, as in {title:uppercase()}. To display it in title case, use {title:titlecase()}.

Functions go into the format part of the template, after the : and before the first | or the closing } if no prefix/suffix is used. If you have both a format and a function reference, the function comes after a second :. Functions return the value of the column specified in the template, suitably modified.

The syntax for using functions is one of:

{lookup_name:function(arguments)}
{lookup_name:format:function(arguments)}
{lookup_name:function(arguments)|prefix|suffix}
{lookup_name:format:function(arguments)|prefix|suffix}

Function names must always be followed by opening and closing parentheses. Some functions require extra values (arguments), and these go inside the parentheses. Arguments are separated by commas. Literal commas (commas as text, not argument separators) must be preceded by a backslash (\) . The last (or only) argument cannot contain a textual closing parenthesis.

Functions are evaluated before format specifications and the prefix/suffix. See further down for an example of using both a format and a function.

Important: If you have programming experience, please note that the syntax in Single Function Mode is not what you expect. Strings are not quoted and spaces are significant. All arguments are considered to be constants; there are no expressions.

Do not use subtemplates (`{ … }`) as function arguments. Instead, use Template Program Mode and General Program Mode.

Some functions require regular expressions. In the template language regular expression matching is case-insensitive.

In the function documentation below, the notation [something]* means that something can be repeated zero or more times. The notation [something]+ means that the something is repeated one or more times (must exist at least one time).

The functions intended for use in Single Function Mode are:

capitalize() – returns the value with the first letter upper case and the rest lower case.

contains(pattern, text if match, text if not match) – checks if the value is matched by the regular expression pattern. Returns text if match if the pattern matches the value, otherwise returns text if no match.

count(separator) – interprets the value as a list of items separated by separator and returns the number of items in the list. Most lists use a comma as the separator, but authors uses an ampersand (&). Examples: {tags:count(,)}, {authors:count(&)}. Aliases: count(), list_count()

format_number(template) – interprets the value as a number and formats that number using a Python formatting template such as {0:5.2f} or {0:,d} or ${0:5,.2f}. The formatting template must begin with {0: and end with } as in the above examples. Exception: you can leave off the leading “{0:” and trailing “}” if the format template contains only a format. See the template language and the Python documentation [https://docs.python.org/3/library/string.html#formatstrings] for more examples. Returns the empty string if formatting fails.

human_readable() – değerin bir sayı olmasını bekler ve bu sayıyı KB, MB, GB vs. biçiminde temsil eden bir karakter dizisi döndürür

ifempty(text if empty) – if the value is not empty then return the value of the field, otherwise return text if empty.

in_list(separator, [ pattern, found_val, ]* not_found_val) – interpret the value as a list of items separated by separator, checking the pattern against each item in the list. If the pattern matches an item then return found_val, otherwise return not_found_val. The pair pattern and found_value can be repeated as many times as desired, permitting returning different values depending on the item’s value. The patterns are checked in order, and the first match is returned.

language_strings(localize) – return the language names [https://www.loc.gov/standards/iso639-2/php/code_list.php] for the language codes [https://www.loc.gov/standards/iso639-2/php/code_list.php] passed in as the value. Example: {languages:language_strings()}. If localize is zero, return the strings in English. If localize is not zero, return the strings in the language of the current locale. Lang_codes is a comma-separated list.

list_item(index, separator) – interpret the value as a list of items separated by separator, returning the ‘index’th item. The first item is number zero. The last item has the index -1 as in list_item(-1,separator). If the item is not in the list, then the empty string is returned.

lookup([ pattern, key, ]* else_key) – The patterns will be checked against the value in order. If a pattern matches then the value of the field named by key is returned. If no pattern matches then the value of the field named by else_key is returned. See``switch`` (below).

lowercase() – returns the value of the field in lower case.

rating_to_stars(use_half_stars) – Returns the rating as string of star (★) characters. The value must be a number between 0 and 5. Set use_half_stars to 1 if you want half star characters for fractional numbers available with custom ratings columns.

re(pattern, replacement) – return the value after applying the regular expression. All instances of pattern in the value are replaced with replacement. The template language uses case insensitive Python regular expressions [https://docs.python.org/3/library/re.html].

select(key) – interpret the value as a comma-separated list of items with each item having the form id:value (the calibre identifier format). The function finds the first pair with the id equal to key and returns the corresponding value. If no id matches then the function returns the empty string.

shorten(left chars, middle text, right chars) – Return a shortened version of the value, consisting of left chars characters from the beginning of the value, followed by middle text, followed by right chars characters from the end of the value. Left chars and right chars must be non-negative integers. Example: assume you want to display the title with a length of at most 15 characters in length. One template that does this is {title:shorten(9,-,5)}. For a book with the title Ancient English Laws in the Times of Ivanhoe the result will be Ancient E-anhoe: the first 9 characters of the title, a -, then the last 5 characters. If the value’s length is less than left chars + right chars + the length of middle text then the value will be returned unchanged. For example, the title The Dome would not be changed.

str_in_list(separator, [ string, found_val, ]+ not_found_val) – interpret the value as a list of items separated by separator then compare string against each value in the list. The string is not a regular expression. If string is equal to any item (ignoring case) then return the corresponding found_val. If string contains separators then it is also treated as a list and each subvalue is checked. The string and found_value pairs can be repeated as many times as desired, permitting returning different values depending on string’s value. If none of the strings match then not_found_value is returned. The strings are checked in order. The first match is returned.

subitems(start_index, end_index) – This function breaks apart lists of tag-like hierarchical items such as genres. It interprets the value as a comma-separated list of tag-like items, where each item is a period-separated list. It returns a new list made by extracting from each item the components from start_index to end_index, then merging the results back together. Duplicates are removed. The first subitem in a period-separated list has an index of zero. If an index is negative then it counts from the end of the list. As a special case, an end_index of zero is assumed to be the length of the list.

Examples:

- Assuming a #genre column containing A.B.C:

- {#genre:subitems(0,1)} returns “A”

- {#genre:subitems(0,2)} returns “A.B”

- {#genre:subitems(1,0)} returns “B.C”

- Assuming a #genre column containing “A.B.C, D.E”:

- {#genre:subitems(0,1)} returns “A, D”

- {#genre:subitems(0,2)} returns “A.B, D.E”

sublist(start_index, end_index, separator) – interpret the value as a list of items separated by separator, returning a new list made from the items from start_index to end_index. The first item is number zero. If an index is negative, then it counts from the end of the list. As a special case, an end_index of zero is assumed to be the length of the list.

Examples assuming that the tags column (which is comma-separated) contains “A, B ,C”:

- {tags:sublist(0,1,\,)} returns “A”

- {tags:sublist(-1,0,\,)} returns “C”

- {tags:sublist(0,-1,\,)} returns “A, B”

swap_around_articles(separator) – returns the value with articles moved to the end. The value can be a list, in which case each item in the list is processed. If the value is a list then you must provide the separator. If no separator is provided then the value is treated as being a single value, not a list. The articles are those used by calibre to generate the title_sort.

swap_around_comma() – given a value of the form B, A, return A B. This is most useful for converting names in LN, FN format to FN LN. If there is no comma in the value then the function returns the value unchanged.

switch([pattern, value,]+ else_value) – for each pattern, value pair, checks if the value matches the regular expression pattern and if so returns the associated value. If no pattern matches, then else_value is returned. You can have as many pattern, value pairs as you wish. The first match is returned.

test(text if not empty, text if empty) – return text if not empty if the value is not empty, otherwise return text if empty.

titlecase() – returns the value of the field in title case.

transliterate() – Return a string in a latin alphabet formed by approximating the sound of the words in the source field. For example, if the source field is Фёдор Миха́йлович Достоевский this function returns Fiodor Mikhailovich Dostoievskii.

uppercase() – returns the value of the field in upper case.

Using functions and formatting in the same template

Suppose you have an integer custom column #myint that you want displayed with leading zeros, as in 003. One way to do this is to use a format of 0>3s. However, by default if a number (integer or float) equals zero then the value is displayed as the empty string so zero values will produce the empty string, not 000. If you want to see 000 values then you use both the format string and the ifempty function to change the empty value back to a zero. The template would be:

{#myint:0>3s:ifempty(0)}

Note that you can use the prefix and suffix as well. If you want the number to appear as [003] or [000], then use the template:

{#myint:0>3s:ifempty(0)|[|]}

General Program Mode

General Program Mode (GPM) replaces template expressions with a program written in the template language. The syntax of the language is defined by the following grammar:

program ::= 'program:' expression_list expression_list ::= top_expression [ ';' top_expression ]* top_expression ::= or_expression or_expression ::= and_expression [ '||' and_expression ]* and_expression ::= not_expression [ '&&' not_expression ]* not_expression ::= [ '!' not_expression ]* | concatenate_expr concatenate_expr::= compare_expr [ '&' compare_expr ]* compare_expr ::= add_sub_expr [ compare_op add_sub_expr ] compare_op ::= '==' | '!=' | '>=' | '>' | '<=' | '<' | 'in' | 'inlist' | '==#' | '!=#' | '>=#' | '>#' | '<=#' | '<#' add_sub_expr ::= times_div_expr [ add_sub_op times_div_expr ]* add_sub_op ::= '+' | '-' times_div_expr ::= unary_op_expr [ times_div_op unary_op_expr ]* times_div_op ::= '*' | '/' unary_op_expr ::= [ add_sub_op unary_op_expr ]* | expression expression ::= identifier | constant | function | assignment | field_reference | if_expr | for_expr | break_expr | continue_expr | '(' expression_list ')' | function_def field_reference ::= '$' [ '$' ] [ '#' ] identifier identifier ::= id_start [ id_rest ]* id_start ::= letter | underscore id_rest ::= id_start | digit constant ::= " string " | ' string ' | number function ::= identifier '(' expression_list [ ',' expression_list ]* ')' function_def ::= 'def' identifier '(' top_expression [ ',' top_expression ]* ')' ':' expression_list 'fed' assignment ::= identifier '=' top_expression if_expr ::= 'if' condition 'then' expression_list [ elif_expr ] [ 'else' expression_list ] 'fi' condition ::= top_expression elif_expr ::= 'elif' condition 'then' expression_list elif_expr | '' for_expr ::= for_list | for_range for_list ::= 'for' identifier 'in' list_expr [ 'separator' separator_expr ] ':' expression_list 'rof' for_range ::= 'for' identifier 'in' range_expr ':' expression_list 'rof' range_expr ::= 'range' '(' [ start_expr ',' ] stop_expr [ ',' step_expr [ ',' limit_expr ] ] ')' list_expr ::= top_expression break_expr ::= 'break' continue_expr ::= 'continue' separator_expr ::= top_expression start_expr ::= top_expression stop_expr ::= top_expression step_expr ::= top_expression limit_expr ::= top_expression

Notes:

- a top_expression always has a value. The value of an expression_list is the value of the last top_expression in the list. For example, the value of the expression list 1;2;'foobar';3 is 3.

- In a logical context, any non-empty value is True

- In a logical context, the empty value is False

- Strings and numbers can be used interchangeably. For example, 10 and '10' are the same thing.

- Comments are lines starting with a ‘#’ character. Comments beginning later in a line are not supported.

Operator precedence

The operator precedence (order of evaluation) from highest (evaluated first) to lowest (evaluated last) is:

- Function calls, constants, parenthesized expressions, statement expressions, assignment expressions, field references.

- Unary plus (+) and minus (-). These operators evaluate right to left.

These and all the other arithmetic operators return integers if the expression results in a fractional part equal to zero. For example, if an expression returns 3.0 it is changed to 3.

- Multiply (*) and divide (/). These operators are associative and evaluate left to right. Use parentheses if you want to change the order of evaluation.

- Add (+) and subtract (-). These operators are associative and evaluate left to right.

- Numeric and string comparisons. These operators return '1' if the comparison succeeds, otherwise the empty string (''). Comparisons are not associative: a < b < c is a syntax error.

- String concatenation (&). The & operator returns a string formed by concatenating the left-hand and right-hand expressions. Example: 'aaa' & 'bbb' returns 'aaabbb'. The operator is associative and evaluates left to right.

- Unary logical not (!). This operator returns '1' if the expression is False (evaluates to the empty string), otherwise ''.

- Logical and (&&). This operator returns ‘1’ if both the left-hand and right-hand expressions are True, or the empty string '' if either is False. It is associative, evaluates left to right, and does short-circuiting [https://chortle.ccsu.edu/java5/Notes/chap40/ch40_2.html].

- Logical or (||). This operator returns '1' if either the left-hand or right-hand expression is True, or '' if both are False. It is associative, evaluates left to right, and does short-circuiting [https://chortle.ccsu.edu/java5/Notes/chap40/ch40_2.html]. It is an inclusive or, returning '1' if both the left- and right-hand expressions are True.

Field references

A field_reference evaluates to the value of the metadata field named by lookup name that follows the $ or $$. Using $ is equivalent to using the field() function. Using $$ is equivalent to using the raw_field function. Examples:

* $authors ==> field('authors')
* $#genre ==> field('#genre')
* $$pubdate ==> raw_field('pubdate')
* $$#my_int ==> raw_field('#my_int')

If expressions

If expressions first evaluate the condition. If the condition is True (a non-empty value) then the expression_list in the then clause is evaluated. If it is False then if present the expression_list in the elif or else clause is evaluated. The elif and else parts are optional. The words if, then, elif, else, and fi are reserved; you cannot use them as identifier names. You can put newlines and white space wherever they make sense. The condition is a top_expression not an expression_list; semicolons are not allowed. The expression_lists are semicolon-separated sequences of top_expressions. An if expression returns the result of the last top_expression in the evaluated expression_list, or the empty string if no expression list was evaluated.

Examples:

* program: if field('series') then 'yes' else 'no' fi
* program:
if field('series') then
a = 'yes';
b = 'no'
else
a = 'no';
b = 'yes'
fi;
strcat(a, '-', b)

Nested if example:

program:
if field('series') then
if check_yes_no(field('#mybool'), '', '', '1') then
'yes'
else
'no'
fi
else
'no series'
fi

As said above, an if produces a value. This means that all the following are equivalent:

* program: if field('series') then 'foo' else 'bar' fi
* program: if field('series') then a = 'foo' else a = 'bar' fi; a
* program: a = if field('series') then 'foo' else 'bar' fi; a

As a last example, this program returns the value of the series column if the book has a series, otherwise the value of the title column:

program: field(if field('series') then 'series' else 'title' fi)

For expressions

The for expression iterates over a list of values, processing them one at a time. The list_expression must evaluate either to a metadata field lookup name e.g., tags or #genre, or to a list of values. The range() function (see below) generates a list of numbers. If the result is a valid lookup name then the field’s value is fetched and the separator specified for that field type is used. If the result isn’t a valid lookup name then it is assumed to be a list of values. The list is assumed to be separated by commas unless the optional keyword separator is supplied, in which case the list values must be separated by the result of evaluating the separator_expr. A separator cannot be used if the list is generated by range(). Each value in the list is assigned to the specified variable then the expression_list is evaluated. You can use break to jump out of the loop, and continue to jump to the beginning of the loop for the next iteration.

Example: This template removes the first hierarchical name for each value in Genre (#genre), constructing a list with the new names:

program:
new_tags = '';
for i in '#genre':
j = re(i, '^.*?\.(.*)$', '\1');
new_tags = list_union(new_tags, j, ',')
rof;
new_tags

If the original Genre is History.Military, Science Fiction.Alternate History, ReadMe then the template returns Military, Alternate History, ReadMe. You could use this template in calibre’s Edit metadata in bulk  →  Search & replace with Search for set to template to strip off the first level of the hierarchy and assign the resulting value to Genre.

Note: the last line in the template, new_tags, isn’t strictly necessary in this case because for returns the value of the last top_expression in the expression list. The value of an assignment is the value of its expression, so the value of the for statement is what was assigned to new_tags.

Function definition

If you have code in a template that repeats then you can put that code into a local function. The def keyword starts the definition. It is followed by the function name, the argument list, then the code in the function. The function definition ends with the fed keyword.

Arguments are positional. When a function is called the supplied arguments are matched left to right against the defined parameters, with the value of the argument assigned to the parameter. It is an error to provide more arguments than defined parameters. Parameters can have default values, such as a = 25. If an argument is not supplied for that parameter then the default value is used, otherwise the parameter is set to the empty string.

The return statement can be used in a local function.

A function must be defined before it can be used.

Example: This template computes an approximate duration in years, months, and days from a number of days. The function to_plural() formats the computed values. Note that the example also uses the & operator:

program:
days = 2112;
years = floor(days/360);
months = floor(mod(days, 360)/30);
days = days - ((years*360) + (months * 30));

def to_plural(v, str):
if v == 0 then return '' fi;
return v & ' ' & (if v == 1 then str else str & 's' fi) & ' '
fed;

to_plural(years, 'year') & to_plural(months, 'month') & to_plural(days,'day')

Relational operators

Relational operators return '1' if the comparison is true, otherwise the empty string (‘’).

There are two forms of relational operators: string comparisons and numeric comparisons.

String comparisons do case-insensitive string comparison using lexical order. The supported string comparison operators are ==, !=, <, <=, >, >=, in, and inlist. For the in operator, the result of the left hand expression is interpreted as a regular expression pattern. The in operator is True if the value of left-hand regular expression matches the value of the right hand expression. The inlist operator is true if the left hand regular expression matches any one of the items in the right hand list where the items in the list are separated by commas. The matches are case-insensitive.

The numeric comparison operators are ==#, !=#, <#, <=#, >#, >=#. The left and right expressions must evaluate to numeric values with two exceptions: both the string value “None” (undefined field) and the empty string evaluate to the value zero.

Examples:

- program: field('series') == 'foo' returns '1' if the book’s series is ‘foo’, otherwise ''.

- program: 'f.o' in field('series') returns '1' if the book’s series matches the regular expression f.o (e.g., foo, Off Onyx, etc.), otherwise ''.

- program: 'science' inlist field('#genre') returns '1' if any of the book’s genres match the regular expression science, e.g., Science, History of Science, Science Fiction etc., otherwise ''.

- program: '^science$' inlist field('#genre') returns '1' if any of the book’s genres exactly match the regular expression ^science$, e.g., Science. The genres History of Science and Science Fiction don’t match. If there isn’t a match then returns ''.

- program: if field('series') != 'foo' then 'bar' else 'mumble' fi returns 'bar' if the book’s series is not foo. Otherwise it returns 'mumble'.

- program: if field('series') == 'foo' || field('series') == '1632' then 'yes' else 'no' fi returns 'yes' if series is either 'foo' or '1632', otherwise 'no'.

- program: if '^(foo|1632)$' in field('series') then 'yes' else 'no' fi returns 'yes' if series is either 'foo' or '1632', otherwise 'no'.

- program: if 11 > 2 then 'yes' else 'no' fi returns 'no' because the > operator does a lexical comparison.

- program: if 11 ># 2 then 'yes' else 'no' fi returns 'yes' because the ># operator does a numeric comparison.

Additional available functions

The following functions are available in addition to those described in Single Function Mode.

In GPM the functions described in Single Function Mode all require an additional first parameter specifying the value to operate upon. All parameters are expression_lists (see the grammar above).

add(x [, y]*) – returns the sum of its arguments. Throws an exception if an argument is not a number. In most cases you can use the + operator instead of this function.

and(value [, value]*) – returns the string “1” if all values are not empty, otherwise returns the empty string. You can have as many values as you want. In most cases you can use the && operator instead of this function. One reason not to replace and with && is if short-circuiting can change the results because of side effects. For example, and(a='',b=5) will always do both assignments, where the && operator won’t do the second.

assign(id, val) – assigns val to id, then returns val. id must be an identifier, not an expression. In most cases you can use the = operator instead of this function.

approximate_formats() – return a comma-separated list of formats associated with the book. There is no guarantee that the list is correct, although it probably is. This and other zero-parameter functions can be called in Template Program Mode (see below) using the template {:'approximate_formats()'}. Note that resulting format names are always uppercase, as in EPUB. The approximate_formats() function is significantly faster than the formats_... functions discussed below.

author_links(val_separator, pair_separator) – returns a string containing a list of authors and those authors’ link values in the form:

author1 val_separator author1_link pair_separator author2 val_separator author2_link etc.

An author is separated from its link value by the val_separator string with no added spaces. author:linkvalue pairs are separated by the pair_separator string argument with no added spaces. It is up to you to choose separator strings that do not occur in author names or links. An author is included even if the author link is empty.

author_sorts(val_separator) – returns a string containing a list of author’s sort values for the authors of the book. The sort is the one in the author metadata information (different from the author_sort in books). The returned list has the form author sort 1 val_separator author sort 2 etc. with no added spaces. The author sort values in this list are in the same order as the authors of the book. If you want spaces around val_separator then include them in the val_separator string.

book_count(query, use_vl) – returns the count of books found by searching for query. If use_vl is 0 (zero) then virtual libraries are ignored. This function and its companion book_values() are particularly useful in template searches, supporting searches that combine information from many books such as looking for series with only one book. It cannot be used in composite columns unless the tweak allow_template_database_functions_in_composites is set to True. It can be used only in the GUI.

For example this template search uses this function and its companion to find all series with only one book:

1. Define a stored template (using Preferences → Advanced → Template functions) named series_only_one_book (the name is arbitrary). The template is:

program:
vals = globals(vals='');
if !vals then
all_series = book_values('series', 'series:true', ',', 0);
for series in all_series:
if book_count('series:="' & series & '"', 0) == 1 then
vals = list_join(',', vals, ',', series, ',')
fi
rof;
set_globals(vals)
fi;
str_in_list(vals, ',', $series, 1, '')

The first time the template runs (the first book checked) it stores the results of the database lookups in a global template variable named vals. These results are used to check subsequent books without redoing the lookups.

2. Use the stored template in a template search:

template:"program: series_only_one_book()#@#:n:1"

Using a stored template instead of putting the template into the search eliminates problems caused by the requirement to escape quotes in search expressions.

book_values(column, query, sep, use_vl) – returns a list of the unique values contained in the column column (a lookup name), separated by sep, in the books found by searching for query. If use_vl is 0 (zero) then virtual libraries are ignored. This function and its companion book_count() are particularly useful in template searches, supporting searches that combine information from many books such as looking for series with only one book. It cannot be used in composite columns unless the tweak allow_template_database_functions_in_composites is set to True. It can be used only in the GUI.

booksize() – calibre ‘size’ alanının değerini döndürür. Herhangi biçim yoksa ‘’ döndürür.

check_yes_no(field_name, is_undefined, is_false, is_true) – checks if the value of the yes/no field named by the lookup name field_name is one of the values specified by the parameters, returning 'yes' if a match is found otherwise returning the empty string. Set the parameter is_undefined, is_false, or is_true to 1 (the number) to check that condition, otherwise set it to 0. Example:

check_yes_no("#bool", 1, 0, 1) returns 'Yes' if the yes/no field #bool is either True or undefined (neither True nor False).

More than one of is_undefined, is_false, or is_true can be set to 1.

ceiling(x) – returns the smallest integer greater than or equal to x. Throws an exception if x is not a number.

character(character_name) – returns the character named by character_name. For example, character('newline') returns a newline character ('\n'). The supported character names are newline, return, tab, and backslash.

cmp(x, y, lt, eq, gt) – compares x and y after converting both to numbers. Returns lt if x <# y, eq if x ==# y, otherwise gt. This function can usually be replaced with one of the numeric compare operators (==#, <#, >#, etc).

connected_device_name(storage_location_key) – if a device is connected then return the device name, otherwise return the empty string. Each storage location on a device has its own device name. The storage_location_key names are 'main', 'carda' and 'cardb'. This function works only in the GUI.

connected_device_uuid(storage_location_key) – if a device is connected then return the device uuid (unique id), otherwise return the empty string. Each storage location on a device has a different uuid. The storage_location_key location names are 'main', 'carda' and 'cardb'. This function works only in the GUI.

current_library_name() – return the last name on the path to the current calibre library.

current_library_path() – return the full path to the current calibre library.

current_virtual_library_name() – return the name of the current virtual library if there is one, otherwise the empty string. Library name case is preserved. Example: program: current_virtual_library_name(). This function works only in the GUI.

date_arithmetic(date, calc_spec, fmt) – Calculate a new date from date using calc_spec. Return the new date formatted according to optional fmt: if not supplied then the result will be in ISO format. The calc_spec is a string formed by concatenating pairs of vW (valueWhat) where v is a possibly-negative number and W is one of the following letters:

- s: add v seconds to date

- m: add v minutes to date

- h: add v hours to date

- d: add v days to date

- w: add v weeks to date

- y: add v years to date, where a year is 365 days.

Example: '1s3d-1m' will add 1 second, add 3 days, and subtract 1 minute from date.

days_between(date1, date2) – date1 ile date2 arasındaki gün sayısını döndürür. date1 date2 den büyükse sayı pozitiftir, değilse negatiftir. date1 veya date2 tarih değillerse, fonksiyon boş karakter dizisi döndürür.

divide(x, y) – returns x / y. Throws an exception if either x or y are not numbers. This function can usually be replaced by the / operator.

eval(string) – evaluates the string as a program, passing the local variables. This permits using the template processor to construct complex results from local variables. In Template Program Mode, because the { and } characters are interpreted before the template is evaluated you must use [[ for the { character and ]] for the } character. They are converted automatically. Note also that prefixes and suffixes (the |prefix|suffix syntax) cannot be used in the argument to this function when using Template Program Mode.

extra_file_size(file_name) – returns the size in bytes of the extra file file_name in the book’s data/ folder if it exists, otherwise -1. See also the functions has_extra_files(), extra_file_names() and extra_file_modtime(). This function can be used only in the GUI.

extra_file_modtime(file_name, format_string) – returns the modification time of the extra file file_name in the book’s data/ folder if it exists, otherwise -1. The modtime is formatted according to format_string (see format_date() for details). If format_string is the empty string, returns the modtime as the floating point number of seconds since the epoch. See also the functions has_extra_files(), extra_file_names() and extra_file_size(). The epoch is OS dependent. This function can be used only in the GUI.

extra_file_names(sep [, pattern]) returns a sep-separated list of extra files in the book’s data/ folder. If the optional parameter pattern, a regular expression, is supplied then the list is filtered to files that match pattern. The pattern match is case insensitive. See also the functions has_extra_files(), extra_file_modtime() and extra_file_size(). This function can be used only in the GUI.

field(lookup_name) – returns the value of the metadata field with lookup name lookup_name.

field_exists(field_name) – checks if a field (column) with the lookup name field_name exists, returning '1' if so and the empty string if not.

finish_formatting(val, fmt, prefix, suffix) – apply the format, prefix, and suffix to a value in the same way as done in a template like {series_index:05.2f| - |- }. This function is provided to ease conversion of complex single-function- or template-program-mode templates to GPM Templates. For example, the following program produces the same output as the above template:

program: finish_formatting(field("series_index"), "05.2f", " - ", " - ")

Başka bir örnek: {series:re(([^\s])[^\s]+(\s|$),\1)}{series_index:0>2s| - | - }{title} şablonu için şunu kullanın:

program:
strcat(
re(field('series'), '([^\s])[^\s]+(\s|$)', '\1'),
finish_formatting(field('series_index'), '0>2s', ' - ', ' - '),
field('title')
)

first_matching_cmp(val, [ cmp, result, ]* else_result) – compares val < cmp in sequence, returning the associated result for the first comparison that succeeds. Returns else_result if no comparison succeeds. Example:

i = 10;
first_matching_cmp(i,5,"small",10,"middle",15,"large","giant")

returns "large". The same example with a first value of 16 returns "giant".

first_non_empty(value [, value]*) – returns the first value that is not empty. If all values are empty, then the empty string is returned. You can have as many values as you want.

floor(x) – returns the largest integer less than or equal to x. Throws an exception if x is not a number.

format_date(val, format_string) – format the value, which must be a date string, using the format_string, returning a string. The formatting codes are:

- d    : the day as number without a leading zero (1 to 31)

- dd   : the day as number with a leading zero (01 to 31)

- ddd  : the abbreviated localized day name (e.g. “Mon” to “Sun”).

- dddd : the long localized day name (e.g. “Monday” to “Sunday”).

- M    : the month as number without a leading zero (1 to 12).

- MM   : the month as number with a leading zero (01 to 12)

- MMM  : the abbreviated localized month name (e.g. “Jan” to “Dec”).

- MMMM : the long localized month name (e.g. “January” to “December”).

- yy   : the year as two digit number (00 to 99).

- yyyy : the year as four digit number.

- h    : the hours without a leading 0 (0 to 11 or 0 to 23, depending on am/pm)

- hh   : the hours with a leading 0 (00 to 11 or 00 to 23, depending on am/pm)

- m    : the minutes without a leading 0 (0 to 59)

- mm   : the minutes with a leading 0 (00 to 59)

- s    : the seconds without a leading 0 (0 to 59)

- ss   : the seconds with a leading 0 (00 to 59)

- ap   : use a 12-hour clock instead of a 24-hour clock, with ‘ap’ replaced by the localized string for am or pm.

- AP   : use a 12-hour clock instead of a 24-hour clock, with ‘AP’ replaced by the localized string for AM or PM.

- iso  : the date with time and timezone. Must be the only format present.

- to_number   : convert the date & time into a floating point number (a timestamp)

- from_number : convert a floating point number (a timestamp) into an iso formatted date. If you want a different date format then add the desired formatting string after from_number and a colon (:). Example: from_number:MMM dd yyyy

You might get unexpected results if the date you are formatting contains localized month names, which can happen if you changed the date format tweaks to contain MMMM. In this case, instead of using the field() function as in:

format_date(field('pubdate'), 'yyyy')

use the raw_field() function as in:

format_date(raw_field('pubdate'), 'yyyy')

format_date_field(field_name, format_string) – format the value in the field field_name, which must be the lookup name of date field, either standard or custom. See format_date() for the formatting codes. This function is much faster than format_date and should be used when you are formatting the value in a field (column). It can’t be used for computed dates or dates in string variables. Examples:

format_date_field('pubdate', 'yyyy.MM.dd')
format_date_field('#date_read', 'MMM dd, yyyy')

formats_modtimes(date_format_string) – return a comma-separated list of colon-separated items FMT:DATE representing modification times for the formats of a book. The date_format_string parameter specifies how the date is to be formatted. See the format_date() function for details. You can use the select function to get the modification time for a specific format. Note that format names are always uppercase, as in EPUB.

formats_paths() – return a comma-separated list of colon-separated items FMT:PATH giving the full path to the formats of a book. You can use the select function to get the path for a specific format. Note that format names are always uppercase, as in EPUB.

formats_sizes() – return a comma-separated list of colon-separated FMT:SIZE items giving the sizes in bytes of the formats of a book. You can use the select function to get the size for a specific format. Note that format names are always uppercase, as in EPUB.

fractional_part(x) – returns the value after the decimal point. For example, fractional_part(3.14) returns 0.14. Throws an exception if x is not a number.

get_link(field_name, field_value) – fetch the link for field field_name with value field_value. If there is no attached link, return the empty string. Examples:

- The following returns the link attached to the tag Fiction:

get_link('tags', 'Fiction')

- This template makes a list of the links for all the tags associated with a book in the form value:link, ...:

program:
ans = '';
for t in $tags:
l = get_link('tags', t);
if l then
ans = list_join(', ', ans, ',', t & ':' & get_link('tags', t), ',')
fi
rof;
ans

has_cover() – return 'Yes' if the book has a cover, otherwise the empty string.

has_extra_files([pattern]) – returns the count of extra files, otherwise ‘’ (the empty string). If the optional parameter pattern (a regular expression) is supplied then the list is filtered to files that match pattern before the files are counted. The pattern match is case insensitive. See also the functions extra_file_names(), extra_file_size() and extra_file_modtime(). This function can be used only in the GUI.

identifier_in_list(val, id_name [, found_val, not_found_val]) – treat val as a list of identifiers separated by commas. An identifier has the format id_name:value. The id_name parameter is the id_name text to search for, either id_name or id_name:regexp. The first case matches if there is any identifier matching that id_name. The second case matches if id_name matches an identifier and the regexp matches the identifier’s value. If found_val and not_found_val are provided then if there is a match then return found_val, otherwise return not_found_val. If found_val and not_found_val are not provided then if there is a match then return the identifier:value pair, otherwise the empty string ('').

is_marked() – check whether the book is marked in calibre. If it is then return the value of the mark, either 'true' (lower case) or a comma-separated list of named marks. Returns '' (the empty string) if the book is not marked. This function works only in the GUI.

language_codes(lang_strings) – return the language codes [https://www.loc.gov/standards/iso639-2/php/code_list.php] for the language names passed in lang_strings. The strings must be in the language of the current locale. Lang_strings is a comma-separated list.

list_contains(value, separator, [ pattern, found_val, ]* not_found_val) – (Alias of in_list) Interpreting the value as a list of items separated by separator, evaluate the pattern against each value in the list. If the pattern matches any value then return found_val, otherwise return not_found_val. The pattern and found_value can be repeated as many times as desired, permitting returning different values depending on the search. The patterns are checked in order. The first match is returned. Aliases: in_list(), list_contains()

list_count(value, separator) – interprets value as a list of items separated by separator, returning the count of items in the list. Aliases: count(), list_count()

list_count_matching(list, pattern, separator) – interprets list as a list of items separated by separator, returning the number of items in the list that match the regular expression pattern. Aliases: list_count_matching(), count_matching()

list_difference(list1, list2, separator) – return a list made by removing from list1 any item found in list2 using a case-insensitive comparison. The items in list1 and list2 are separated by separator, as are the items in the returned list.

list_equals(list1, sep1, list2, sep2, yes_val, no_val) – return yes_val if list1 and list2 contain the same items, otherwise return no_val. The items are determined by splitting each list using the appropriate separator character (sep1 or sep2). The order of items in the lists is not relevant. The comparison is case-insensitive.

list_intersection(list1, list2, separator) – return a list made by removing from list1 any item not found in list2, using a case-insensitive comparison. The items in list1 and list2 are separated by separator, as are the items in the returned list.

list_join(with_separator, list1, separator1 [, list2, separator2]*) – return a list made by joining the items in the source lists (list1 etc) using with_separator between the items in the result list. Items in each source list[123...] are separated by the associated separator[123...]. A list can contain zero values. It can be a field like publisher that is single-valued, effectively a one-item list. Duplicates are removed using a case-insensitive comparison. Items are returned in the order they appear in the source lists. If items on lists differ only in letter case then the last is used. All separators can be more than one character.

Example:

program:
list_join('#@#', $authors, '&', $tags, ',')

You can use list_join on the results of previous calls to list_join as follows:

program:
a = list_join('#@#', $authors, '&', $tags, ',');
b = list_join('#@#', a, '#@#', $#genre, ',', $#people, '&', 'some value', ',')

You can use expressions to generate a list. For example, assume you want items for authors and #genre, but with the genre changed to the word “Genre: “ followed by the first letter of the genre, i.e. the genre “Fiction” becomes “Genre: F”. The following will do that:

program:
list_join('#@#', $authors, '&', list_re($#genre, ',', '^(.).*$', 'Genre: \1'), ',')

list_re(src_list, separator, include_re, opt_replace) – Construct a list by first separating src_list into items using the separator character. For each item in the list, check if it matches include_re. If it does then add it to the list to be returned. If opt_replace is not the empty string then apply the replacement before adding the item to the returned list.

list_re_group(src_list, separator, include_re, search_re [, template_for_group]*) – Like list_re except replacements are not optional. It uses re_group(item, search_re, template ...) when doing the replacements.

list_remove_duplicates(list, separator) – return a list made by removing duplicate items in list. If items differ only in case then the last is returned. The items in list are separated by separator, as are the items in the returned list.

list_sort(list, direction, separator) – return list sorted using a case-insensitive lexical sort. If direction is zero, list is sorted ascending, otherwise descending. The list items are separated by separator, as are the items in the returned list.

list_split(list_val, sep, id_prefix) – splits list_val into separate values using sep, then assigns the values to local variables named id_prefix_N where N is the position of the value in the list. The first item has position 0 (zero). The function returns the last element in the list.

Example:

list_split('one:two:foo', ':', 'var')

is equivalent to:

var_0 = 'one';
var_1 = 'two';
var_2 = 'foo

list_union(list1, list2, separator) – return a list made by merging the items in list1 and list2, removing duplicate items using a case-insensitive comparison. If items differ in case, the one in list1 is used. The items in list1 and list2 are separated by separator, as are the items in the returned list. Aliases: merge_lists(), list_union()

mod(x, y) – returns the floor of the remainder of x / y. Throws an exception if either x or y is not a number.

multiply(x [, y]*) – returns the product of its arguments. Throws an exception if any argument is not a number. This function can usually be replaced by the * operator.

not(value) – returns the string “1” if the value is empty, otherwise returns the empty string. This function can usually be replaced with the unary not (!) operator.

ondevice() – return the string 'Yes' if ondevice is set, otherwise return the empty string.

or(value [, value]*) – returns the string '1' if any value is not empty, otherwise returns the empty string. You can have as many values as you want. This function can usually be replaced by the || operator. A reason it cannot be replaced is if short-circuiting will change the results because of side effects.

print(a [, b]*) – prints the arguments to standard output. Unless you start calibre from the command line (calibre-debug -g), the output will go into a black hole. The print function always returns its first argument.

range(start, stop, step, limit) – returns a list of numbers generated by looping over the range specified by the parameters start, stop, and step, with a maximum length of limit. The first value produced is ‘start’. Subsequent values next_v = current_v + step. The loop continues while next_v < stop assuming step is positive, otherwise while next_v > stop. An empty list is produced if start fails the test: start >= stop if step is positive. The limit sets the maximum length of the list and has a default of 1000. The parameters start, step, and limit are optional. Calling range() with one argument specifies stop. Two arguments specify start and stop. Three arguments specify start, stop, and step. Four arguments specify start, stop, step and limit. Examples:

range(5) -> '0, 1, 2, 3, 4'
range(0, 5) -> '0, 1, 2, 3, 4'
range(-1, 5) -> '-1, 0, 1, 2, 3, 4'
range(1, 5) -> '1, 2, 3, 4'
range(1, 5, 2) -> '1, 3'
range(1, 5, 2, 5) -> '1, 3'
range(1, 5, 2, 1) -> error(limit exceeded)

raw_field(lookup_name [, optional_default]) – returns the metadata field named by lookup_name without applying any formatting. It evaluates and returns the optional second argument optional_default if the field’s value is undefined (None).

raw_list(lookup_name, separator) – returns the metadata list named by lookup_name without applying any formatting or sorting, with the items separated by separator.

re_group(value, pattern [, template_for_group]*) – return a string made by applying the regular expression pattern to value and replacing each matched instance with the value returned by the corresponding template. In Template Program Mode, like for the template and the eval functions, you use [[ for { and ]] for }.

The following example looks for a series with more than one word and uppercases the first word:

program: re_group(field('series'), "(\S* )(.*)", "{$:uppercase()}", "{$}")'}

round(x) – returns the nearest integer to x. Throws an exception if x is not a number.

series_sort() – seri sıralama değerini döndürür.

strcat(a [, b]*) – can take any number of arguments. Returns a string formed by concatenating all the arguments.

strcat_max(max, string1 [, prefix2, string2]*) – Returns a string formed by concatenating the arguments. The returned value is initialized to string1. Strings made from prefix, string pairs are added to the end of the value as long as the resulting string length is less than max. Prefixes can be empty. Returns string1 even if string1 is longer than max. You can pass as many prefix, string pairs as you wish.

strcmp(x, y, lt, eq, gt) – does a case-insensitive lexical comparison of x and y. Returns lt if x < y, eq if x == y, otherwise gt. This function can often be replaced by one of the lexical comparison operators (==, >, <, etc.)

strcmpcase(x, y, lt, eq, gt) – does a case-sensitive lexical comparison of x and y. Returns lt if x < y, eq if x == y, otherwise gt.

Note: This is NOT the default behavior used by calibre, for example, in the lexical comparison operators (==, >, <, etc.). This function could cause unexpected results, preferably use strcmp() whenever possible.

strlen(value) – Returns the length of the string value.

substr(str, start, end) – returns the start’th through the end’th characters of str. The first character in str is the zero’th character. If end is negative, then it indicates that many characters counting from the right. If end is zero, then it indicates the last character. For example, substr('12345', 1, 0) returns '2345', and substr('12345', 1, -1) returns '234'.

subtract(x, y) – returns x - y. Throws an exception if either x or y are not numbers. This function can usually be replaced by the - operator.

switch_if([test_expression, value_expression,]+ else_expression) – for each test_expression, value_expression pair, checks if test_expression is True (non-empty) and if so returns the result of value_expression. If no test_expression is True then the result of else_expression` is returned. You can have as many ``test_expression, value_expression pairs as you want.

today() – return a date+time string for today (now). This value is designed for use in format_date or days_between, but can be manipulated like any other string. The date is in ISO [https://en.wikipedia.org/wiki/ISO_8601] date/time format.

template(x) – evaluates x as a template. The evaluation is done in its own context, meaning that variables are not shared between the caller and the template evaluation.

to_hex(val) – returns the string val encoded in hex. This is useful when constructing calibre URLs.

urls_from_identifiers(identifiers, sort_results) – given a comma-separated list of identifiers, where an identifier is a colon-separated pair of values (id_name:id_value), returns a comma-separated list of HTML URLs generated from the identifiers. The list not sorted if sort_results is 0 (character or number), otherwise it is sorted alphabetically by the identifier name. The URLs are generated in the same way as the built-in identifiers column when shown in Book details.

More complex programs in template expressions - Template Program Mode

Template Program Mode (TPM) is a blend of General Program Mode and Single Function Mode. TPM differs from Single Function Mode in that it permits writing template expressions that refer to other metadata fields, use nested functions, modify variables, and do arithmetic. It differs from General Program Mode in that the template is contained between { and } characters and doesn’t begin with the word program:. The program portion of the template is a General Program Mode expression list.

Example: assume you want a template to show the series for a book if it has one, otherwise show the value of a custom field #genre. You cannot do this in the Single Function Mode because you cannot make reference to another metadata field within a template expression. In TPM you can, as the following expression demonstrates:

{#series:'ifempty($, field('#genre'))'}

Örnek bir çok şey gösteriyor:

- TPM is used if the expression begins with :' and ends with '}. Anything else is assumed to be in Single Function Mode.

- the variable $ stands for the field named in the template: the expression is operating upon, #series in this case.

- functions must be given all their arguments. There is no default value. For example, the standard built-in functions must be given an additional initial parameter indicating the source field.

- boşluk karakterleri göz ardı edilir ve ifadenin herhangi bir yerinde kullanılabilir.

- sabit karakter dizileri eşleşen tırnak işaretleri arasındadır, ' veyea ".

All the functions listed under Single Function Mode and General Program Mode can be used in TPM.

In TPM, using { and } characters in string literals can lead to errors or unexpected results because they confuse the template processor. It tries to treat them as template expression boundaries, not characters. In some but not all cases you can replace a { with [[ and a } with ]]. Generally, if your program contains { and } characters then you should use General Program Mode.

As with General Program Mode, for functions documented under Single Function Mode you must supply the value the function is to act upon as the first parameter in addition to the documented parameters. In TPM you can use $ to access the value specified by the lookup name for the template expression.

Python Template Mode

Python Template Mode (PTM) lets you write templates using native python and the calibre API [https://manual.calibre-ebook.com/develop.html#api-documentation-for-various-parts-of-calibre]. The database API will be of most use; further discussion is beyond the scope of this manual. PTM templates are faster and can do more complicated operations but you must know how to write code in python using the calibre API.

A PTM template begins with:

python:
def evaluate(book, context):
# book is a calibre metadata object
# context is an instance of calibre.utils.formatter.PythonTemplateContext,
# which currently contains the following attributes:
# db: a calibre legacy database object.
# globals: the template global variable dictionary.
# arguments: is a list of arguments if the template is called by a GPM template, otherwise None.
# funcs: used to call Built-in/User functions and Stored GPM/Python templates.
# Example: context.funcs.list_re_group()

# your Python code goes here
return 'a string'

You can add the above text to your template using the context menu, usually accessed with a right click. The comments are not significant and can be removed. You must use python indenting.

The context object supports str(context) that returns a string of the context’s contents, and context.attributes that returns a list of the attribute names in the context.

The context.funcs attribute allows calling Built-in and User template functions, and Stored GPM/Python templates, so that you can execute them directly in your code. The functions are retrieved using their names. If the name conflicts with a Python keyword, add an underscore to the end of the name. Examples:

context.funcs.list_re_group()
context.funcs.assert_()

Here is an example of a PTM template that produces a list of all the authors for a series. The list is stored in a Column built from other columns, behaves like tags. It shows in Book details and has the on separate lines checked (in Preferences → Look & feel → Book details). That option requires the list to be comma-separated. To satisfy that requirement the template converts commas in author names to semicolons then builds a comma-separated list of authors. The authors are then sorted, which is why the template uses author_sort.

python:
def evaluate(book, context):
if book.series is None:
return ''
db = context.db.new_api
ans = set()
# Get the list of books in the series
ids = db.search(f'series:"={book.series}"', '')
if ids:
# Get all the author_sort values for the books in the series
author_sorts = (v for v in db.all_field_for('author_sort', ids).values())
# Add the names to the result set, removing duplicates
for aus in author_sorts:
ans.update(v.strip() for v in aus.split('&'))
# Make a sorted comma-separated string from the result set
return ', '.join(v.replace(',', ';') for v in sorted(ans))

The output in Book details looks like this:

Stored templates

Both General Program Mode and Python Template Mode support saving templates and calling those templates from another template, much like calling stored functions. You save templates using Preferences → Advanced → Template functions. More information is provided in that dialog. You call a template the same way you call a function, passing positional arguments if desired. An argument can be any expression. Examples of calling a template, assuming the stored template is named foo:

- foo() – call the template passing no arguments.

- foo(a, b) call the template passing the values of the two variables a and b.

- foo(if field('series') then field('series_index') else 0 fi) – if the book has a series then pass the series_index, otherwise pass the value 0.

In GPM you retrieve the arguments passed in the call to the stored template using the arguments function. It both declares and initializes local variables, effectively parameters. The variables are positional; they get the value of the parameter given in the call in the same position. If the corresponding parameter is not provided in the call then arguments assigns that variable the provided default value. If there is no default value then the variable is set to the empty string. For example, the following arguments function declares 2 variables, key, alternate:

arguments(key, alternate='series')

Examples, again assuming the stored template is named foo:

- foo('#myseries') – argument key is assigned the value 'myseries' and the argument alternate is assigned the default value 'series'.

- foo('series', '#genre') the variable key is assigned the value 'series' and the variable alternate is assigned the value '#genre'.

- foo() – the variable key is assigned the empty string and the variable alternate is assigned the value 'series'.

In PTM the arguments are passed in the arguments parameter, which is a list of strings. There isn’t any way to specify default values. You must check the length of the arguments list to be sure that the number of arguments is what you expect.

An easy way to test stored templates is using the Template tester dialog. For ease of access give it a keyboard shortcut in Preferences → Advanced → Keyboard shortcuts → Template tester. Giving the Stored templates dialog a shortcut will help switching more rapidly between the tester and editing the stored template’s source code.

Providing additional information to templates

A developer can choose to pass additional information to the template processor, such as application-specific book metadata or information about what the processor is being asked to do. A template can access this information and use it during the evaluation.

Developer: how to pass additional information

The additional information is a Python dictionary containing pairs variable_name: variable_value where the values must be strings. The template can access the dictionary, creating template local variables named variable_name containing the value variable_value. The user cannot change the name so it is best to use names that won’t collide with other template local variables, for example by prefixing the name with an underscore.

This dictionary is passed to the template processor (the formatter) using the named parameter global_vars=your_dict. The full method signature is:

def safe_format(self, fmt, kwargs, error_value, book,
column_name=None, template_cache=None,
strip_results=True, template_functions=None,
global_vars={})

Template writer: how to access the additional information

You access the additional information (the globals dictionary) in a template using the template function:

globals(id[=expression] [, id[=expression]]*)

where id is any legal variable name. This function checks whether the additional information provided by the developer contains the name. If it does then the function assigns the provided value to a template local variable with that name. If the name is not in the additional information and if an expression is provided, the expression is evaluated and the result is assigned to the local variable. If neither a value nor an expression is provided, the function assigns the empty string ('') to the local variable.

A template can set a value in the globals dictionary using the template function:

set_globals(id[=expression] [, id[=expression]]*)

This function sets the globals dictionary key:value pair id:value where value is the value of the template local variable id. If that local variable doesn’t exist then value is set to the result of evaluating expression.

Notes on the difference between modes

The three program modes, Single Function Mode (SFM), Template Program Mode (TPM), and General Program Mode (GPM), work differently. SFM is intended to be ‘simple’ so it hides a lot of programming language bits.

Differences:

- In SFM the value of the column is always passed as an ‘invisible’ first argument to a function included in the template.

- SFM doesn’t support the difference between variables and strings; all values are strings.

- The following SFM template returns either the series name or the string “no series”:

{series:ifempty(no series)}

The equivalent template in TPM is

{series:'ifempty($, 'no series')'}

The equivalent template in GPM is:

program: ifempty(field('series'), 'no series')

The first argument to ifempty is the value of the field series. The second argument is the string no series. In SFM the first argument, the value of the field, is automatically passed (the invisible argument).

- Several template functions, for example booksize() and current_library_name(), take no arguments. Because of the ‘invisible argument’ you cannot use these functions in SFM.

- Nested functions, where a function calls another function to compute an argument, cannot be used in SFM. For example this template, intended to return the first 5 characters of the series value uppercased, won’t work in SFM:

{series:uppercase(substr(0,5))}

- TPM and GPM support nested functions. The above template in TPM would be:

{series:'uppercase(substr($, 0,5))'}

In GPM it would be:

program: uppercase(substr(field('series'), 0,5))

- As noted in the above Template Program Mode section, using { and } characters in TPM string literals can lead to errors or unexpected results because they confuse the template processor. It tries to treat them as template boundaries, not characters. In some but not all cases you can replace a { with [[ and a } with ]]. Generally, if your program contains { and } characters then you should use General Program Mode.

User-defined Python template functions

You can add your own Python functions to the template processor. Such functions can be used in any of the three template programming modes. The functions are added by going to Preferences  →  Advanced  →  Template functions. Instructions are shown in that dialog.

Kayıt et/gönder şablonları ile ilgili özel notlar

Bir şablon diske kaydet veya aygıta gönder şablonu ile kullanıldığında özel işlemler uygulanır. Alanların değerleri temizlenir, dosya sistemine özel karakterler alt çizgiler ile değiştirilir, yatık çizgiler dahil. Bunun anlamı alan metni kullanılarak klasör oluşturulamayacağıdır. Ancak yatık çizgiler ön ek veya son ek karakter dizilerinden değiştirilmezler, yani bu karakter dizilerindeki yatık çizgiler klasörlerin oluşturulmasına sebep olur. Bu sebepten, değişken derinlikli klasör yapıları oluşturabilirsiniz.

Örneğin, series/series_index - title dizin yapısını istediğimizi varsayın, series’in olmama durumunda, başlık en üst klasörde olmalıdır. Bunu yapan şablon şu şekildedir:

{series:||/}{series_index:|| - }{title}

Yatık çizgi ve tire sadece seriler boş değilken görülür.

The lookup function lets us do even fancier processing. For example, assume that if a book has a series, then we want the folder structure series/series index - title.fmt. If the book does not have a series then we want the folder structure genre/author_sort/title.fmt. If the book has no genre then we want to use ‘Unknown’. We want two completely different paths, depending on the value of series.

Bunu elde etmek için:

1. Create a composite field (give it lookup name #aa) containing {series}/{series_index} - {title}. If the series is not empty, then this template will produce series/series_index - title.

2. Create a composite field (give it lookup name #bb) containing {#genre:ifempty(Unknown)}/{author_sort}/{title}. This template produces genre/author_sort/title, where an empty genre is replaced with Unknown.

3. Set the save template to {series:lookup(.,#aa,#bb)}. This template chooses composite field #aa if series is not empty and composite field #bb if series is empty. We therefore have two completely different save paths, depending on whether or not series is empty.

Tips

- Use the Template Tester to test templates. Add the tester to the context menu for books in the library and/or give it a keyboard shortcut.

- Templates can use other templates by referencing composite columns built with the desired template. Alternatively, you can use Stored Templates.

- Bir santralde, özel şablon {} kullanarak bir alanı boş olarak ayarlayabilirsiniz (ya da boşla eş değer neyse). Bu şablon her zaman boş karakter dizisi olarak değerlenecektir.

- Yukarıda sıfır değeri olsa bile sayıları göstermeye yarayan teknik standart alan series_index ile de çalışır.

Function reference

Reference for all built-in template language functions

Arithmetic

- add(x [, y]*)
- ceiling(x)
- divide(x, y)
- floor(x)
- fractional_part(x)
- mod(x)
- multiply(x [, y]*)
- round(x)
- subtract(x, y)

Boolean

- and(value [, value]*)
- not(value)
- or(value [, value]*)

Date functions

- date_arithmetic(date, calc_spec, fmt)
- days_between(tarih1, tarih2)
- today()

Formatting values

- finish_formatting(val, fmt, önek, sonek)

- format_date(val, format_string)

- format_date_field(field_name, format_string)

- format_number(v, template)
- human_readable(v)
- rating_to_stars(value, use_half_stars)

- urls_from_identifiers(identifiers, sort_results)

Get values from metadata

- annotation_count()
- approximate_formats()
- author_links(val_separator, pair_separator)
- author_sorts(val_separator)
- booksize()
- connected_device_name(storage_location)
- connected_device_uuid(storage_location)
- current_library_name()
- current_library_path()
- current_virtual_library_name()
- field(lookup_name)
- formats_modtimes(date_format)
- formats_paths()
- formats_sizes()
- has_cover()
- is_marked()
- language_codes(lang_strings)
- language_strings(lang_codes, localize)
- ondevice()
- raw_field(lookup_name [, optional_default])
- raw_list(lookup_name, separator)
- series_sort()
- user_categories()
- virtual_libraries()

If-then-else

- check_yes_no(field_name, is_undefined, is_false, is_true)

- contains(val, pattern, text if match, text if not match)

- field_exists(field_name)
- ifempty(val, boşsa metin)
- test(val, text if not empty, text if empty)

Iterating over values

- first_non_empty(value [, value]*)
- lookup(val, [pattern, field,]+ else_field)
- switch(val, [pattern, value,]+ else_value)

- switch_if([test_expression, value_expression,]+ else_expression)

List lookup

- identifier_in_list(val, id_name [, found_val, not_found_val])

- in_list(val, separator, [ pattern, found_val, ]+ not_found_val)

- list_item(val, indis, ayraç)

- select(val, key)

- str_in_list(val, separator, [string, found_val, ]+ not_found_val)

List manipulation

- count(val, separator)

- list_count_matching(list, pattern, separator)

- list_difference(list1, list2, separator)

- list_equals(list1, sep1, list2, sep2, yes_val, no_val)

- list_intersection(list1, list2, separator)

- list_join(with_separator, list1, separator1 [, list2, separator2]*)

- list_re(src_list, ayraç, include_re, opt_replace)

- list_re_group(src_list, separator, include_re, search_re [, group_template]+)

- list_remove_duplicates(list, separator)
- list_sort(list, yön, ayraç)
- list_split(list_val, sep, id_prefix)
- list_union(list1, list2, separator)
- range(start, stop, step, limit)

- subitems(val, başlangıç_indisi, bitiş_indisi)

- sublist(val, start_index, end_index, separator)

Other

- arguments(id[=expression] [, id[=expression]]*)

- assign(id, val)

- globals(id[=expression] [, id[=expression]]*)

- print(a[, b]*)

Recursion

- eval(şablon)

- template(x)

Relational

- cmp(x, y, lt, eq, gt)

- first_matching_cmp(val, [cmp1, result1,]+, else_result)

- strcmp(x, y, lt, eq, gt)

- strcmpcase(x, y, lt, eq, gt)

String case changes

- capitalize(val)
- lowercase(val)
- titlecase(val)
- uppercase(val)

String manipulation

- character(character_name)

- re(val, pattern, replacement)

- re_group(val, pattern [, template_for_group]*)

- shorten(val, left chars, middle text, right chars)

- strcat(a [, b]*)

- strcat_max(max, string1 [, prefix2, string2]*)

- strlen(a)
- substr(str, start, end)
- swap_around_articles(val, separator)
- swap_around_comma(val)
- to_hex(val)
- transliterate(a)

Template database functions

- book_count(query, use_vl)

- book_values(column, query, sep, use_vl)

- extra_file_modtime(file_name, format_string)

- extra_file_names(sep [, pattern])
- extra_file_size(file_name)
- get_link(field_name, field_value)

- get_note(field_name, field_value, plain_text)

- has_extra_files([pattern])

- has_note(field_name, field_value)

other

- set_globals(id[=expression] [, id[=expression]]*)

API of the Metadata objects

- Metadata

- STANDARD_METADATA_FIELDS

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »
- Calibre şablon dili »

- Reference for all built-in template language functions

Reference for all built-in template language functions

Here, we document all the built-in functions available in the calibre template language. Every function is implemented as a class in python and you can click the source links to see the source code, in case the documentation is insufficient. The functions are arranged in logical groups by type.

Arithmetic

- add(x [, y]*)

- ceiling(x)

- divide(x, y)

- floor(x)

- fractional_part(x)

- mod(x)

- multiply(x [, y]*)

- round(x)

- subtract(x, y)

Boolean

- and(value [, value]*)

- not(value)

- or(value [, value]*)

Date functions

- date_arithmetic(date, calc_spec, fmt)

- days_between(tarih1, tarih2)

- today()

Formatting values

- finish_formatting(val, fmt, önek, sonek)

- format_date(val, format_string)

- format_date_field(field_name, format_string)

- format_number(v, template)

- human_readable(v)

- rating_to_stars(value, use_half_stars)

- urls_from_identifiers(identifiers, sort_results)

Get values from metadata

- annotation_count()

- approximate_formats()

- author_links(val_separator, pair_separator)

- author_sorts(val_separator)

- booksize()

- connected_device_name(storage_location)

- connected_device_uuid(storage_location)

- current_library_name()

- current_library_path()

- current_virtual_library_name()

- field(lookup_name)

- formats_modtimes(date_format)

- formats_paths()

- formats_sizes()

- has_cover()

- is_marked()

- language_codes(lang_strings)

- language_strings(lang_codes, localize)

- ondevice()

- raw_field(lookup_name [, optional_default])

- raw_list(lookup_name, separator)

- series_sort()

- user_categories()

- virtual_libraries()

If-then-else

- check_yes_no(field_name, is_undefined, is_false, is_true)

- contains(val, pattern, text if match, text if not match)

- field_exists(field_name)

- ifempty(val, boşsa metin)

- test(val, text if not empty, text if empty)

Iterating over values

- first_non_empty(value [, value]*)

- lookup(val, [pattern, field,]+ else_field)

- switch(val, [pattern, value,]+ else_value)

- switch_if([test_expression, value_expression,]+ else_expression)

List lookup

- identifier_in_list(val, id_name [, found_val, not_found_val])

- in_list(val, separator, [ pattern, found_val, ]+ not_found_val)

- list_item(val, indis, ayraç)

- select(val, key)

- str_in_list(val, separator, [string, found_val, ]+ not_found_val)

List manipulation

- count(val, separator)

- list_count_matching(list, pattern, separator)

- list_difference(list1, list2, separator)

- list_equals(list1, sep1, list2, sep2, yes_val, no_val)

- list_intersection(list1, list2, separator)

- list_join(with_separator, list1, separator1 [, list2, separator2]*)

- list_re(src_list, ayraç, include_re, opt_replace)

- list_re_group(src_list, separator, include_re, search_re [, group_template]+)

- list_remove_duplicates(list, separator)

- list_sort(list, yön, ayraç)

- list_split(list_val, sep, id_prefix)

- list_union(list1, list2, separator)

- range(start, stop, step, limit)

- subitems(val, başlangıç_indisi, bitiş_indisi)

- sublist(val, start_index, end_index, separator)

Other

- arguments(id[=expression] [, id[=expression]]*)

- assign(id, val)

- globals(id[=expression] [, id[=expression]]*)

- print(a[, b]*)

Recursion

- eval(şablon)

- template(x)

Relational

- cmp(x, y, lt, eq, gt)

- first_matching_cmp(val, [cmp1, result1,]+, else_result)

- strcmp(x, y, lt, eq, gt)

- strcmpcase(x, y, lt, eq, gt)

String case changes

- capitalize(val)

- lowercase(val)

- titlecase(val)

- uppercase(val)

String manipulation

- character(character_name)

- re(val, pattern, replacement)

- re_group(val, pattern [, template_for_group]*)

- shorten(val, left chars, middle text, right chars)

- strcat(a [, b]*)

- strcat_max(max, string1 [, prefix2, string2]*)

- strlen(a)

- substr(str, start, end)

- swap_around_articles(val, separator)

- swap_around_comma(val)

- to_hex(val)

- transliterate(a)

Template database functions

- book_count(query, use_vl)

- book_values(column, query, sep, use_vl)

- extra_file_modtime(file_name, format_string)

- extra_file_names(sep [, pattern])

- extra_file_size(file_name)

- get_link(field_name, field_value)

- get_note(field_name, field_value, plain_text)

- has_extra_files([pattern])

- has_note(field_name, field_value)

other

- set_globals(id[=expression] [, id[=expression]]*)

API of the Metadata objects

Arithmetic

add(x [, y]*)

class calibre.utils.formatter_functions.BuiltinAdd[kaynak]

add(x [, y]*) – returns the sum of its arguments. Throws an exception if an argument is not a number. This function can often be replaced with the + operator.

ceiling(x)

class calibre.utils.formatter_functions.BuiltinCeiling[kaynak]

ceiling(x) – returns the smallest integer greater than or equal to x. Throws an exception if x is not a number.

divide(x, y)

class calibre.utils.formatter_functions.BuiltinDivide[kaynak]

divide(x, y) – returns x / y. Throws an exception if either x or y are not numbers. This function can often be replaced with the / operator.

floor(x)

class calibre.utils.formatter_functions.BuiltinFloor[kaynak]

floor(x) – returns the largest integer less than or equal to x. Throws an exception if x is not a number.

fractional_part(x)

class calibre.utils.formatter_functions.BuiltinFractionalPart[kaynak]

fractional_part(x) – returns the value after the decimal point. For example, fractional_part(3.14) returns 0.14. Throws an exception if x is not a number.

mod(x)

class calibre.utils.formatter_functions.BuiltinMod[kaynak]

mod(x) – returns floor(remainder of x / y). Throws an exception if either x or y is not a number.

multiply(x [, y]*)

class calibre.utils.formatter_functions.BuiltinMultiply[kaynak]

multiply(x [, y]*) – returns the product of its arguments. Throws an exception if any argument is not a number. This function can often be replaced with the * operator.

round(x)

class calibre.utils.formatter_functions.BuiltinRound[kaynak]

round(x) – returns the nearest integer to x. Throws an exception if x is not a number.

subtract(x, y)

class calibre.utils.formatter_functions.BuiltinSubtract[kaynak]

subtract(x, y) – returns x - y. Throws an exception if either x or y are not numbers. This function can often be replaced with the - operator.

Boolean

and(value [, value]*)

class calibre.utils.formatter_functions.BuiltinAnd[kaynak]

and(value [, value]*) – returns the string “1” if all values are not empty, otherwise returns the empty string. This function works well with test or first_non_empty. You can have as many values as you want. In many cases the && operator can replace this function.

not(value)

class calibre.utils.formatter_functions.BuiltinNot[kaynak]

not(value) – returns the string “1” if the value is empty, otherwise returns the empty string. This function works well with test or first_non_empty. In many cases the ! operator can replace this function.

or(value [, value]*)

class calibre.utils.formatter_functions.BuiltinOr[kaynak]

or(value [, value]*) – returns the string “1” if any value is not empty, otherwise returns the empty string. This function works well with test or first_non_empty. You can have as many values as you want. In many cases the || operator can replace this function.

Date functions

date_arithmetic(date, calc_spec, fmt)

class calibre.utils.formatter_functions.BuiltinDateArithmetic[kaynak]

date_arithmetic(date, calc_spec, fmt) – Calculate a new date from ‘date’ using ‘calc_spec’. Return the new date formatted according to optional ‘fmt’: if not supplied then the result will be in iso format. The calc_spec is a string formed by concatenating pairs of ‘vW’ (valueWhat) where ‘v’ is a possibly-negative number and W is one of the following letters: s: add ‘v’ seconds to ‘date’ m: add ‘v’ minutes to ‘date’ h: add ‘v’ hours to ‘date’ d: add ‘v’ days to ‘date’ w: add ‘v’ weeks to ‘date’ y: add ‘v’ years to ‘date’, where a year is 365 days. Example: ‘1s3d-1m’ will add 1 second, add 3 days, and subtract 1 minute from ‘date’.

days_between(tarih1, tarih2)

class calibre.utils.formatter_functions.BuiltinDaysBetween[kaynak]

days_between(tarih1, tarih2) – tarih1 ve tarih2 arasındaki gün sayısını döndürür. Tarih1 tarih2’den büyükse değer pozitiftir, aksi halde negatif. tarih1 veya tarih2 tarih değillerse, fonksiyon boş karakter dizisi döndürür.

today()

class calibre.utils.formatter_functions.BuiltinToday[kaynak]

today() – bugün için bir tarih karakter dizisi döndür. Bu değer format_date veya days_between ile kullanılmak üzere tasarlanmıştır, ama herhangi bir karakter dizisi gibi oynanabilir. Tarih ISO biçimindedir.

Formatting values

finish_formatting(val, fmt, önek, sonek)

class calibre.utils.formatter_functions.BuiltinFinishFormatting[kaynak]

finish_formatting(val, fmt, önek, sonek) – Değere biçim, önek ve soneki {series_index:05.2f| - |- } gibi bir şablonda olduğu gibi uygular. Örneğin, şu program aşağıdaki şablonla aynı çıktıyı üretir: program: finish_formatting(field(“series_index”), “05.2f”, “ - “, “ - “)

format_date(val, format_string)

class calibre.utils.formatter_functions.BuiltinFormatDate[kaynak]

format_date(val, format_string) – format the value, which must be a date, using the format_string, returning a string. The formatting codes are: d : the day as number without a leading zero (1 to 31) dd : the day as number with a leading zero (01 to 31) ddd : the abbreviated localized day name (e.g. “Mon” to “Sun”). dddd : the long localized day name (e.g. “Monday” to “Sunday”). M : the month as number without a leading zero (1 to 12). MM : the month as number with a leading zero (01 to 12) MMM : the abbreviated localized month name (e.g. “Jan” to “Dec”). MMMM : the long localized month name (e.g. “January” to “December”). yy : the year as two digit number (00 to 99). yyyy : the year as four digit number. h : the hours without a leading 0 (0 to 11 or 0 to 23, depending on am/pm) hh : the hours with a leading 0 (00 to 11 or 00 to 23, depending on am/pm) m : the minutes without a leading 0 (0 to 59) mm : the minutes with a leading 0 (00 to 59) s : the seconds without a leading 0 (0 to 59) ss : the seconds with a leading 0 (00 to 59) ap : use a 12-hour clock instead of a 24-hour clock, with “ap” replaced by the localized string for am or pm AP : use a 12-hour clock instead of a 24-hour clock, with “AP” replaced by the localized string for AM or PM iso : the date with time and timezone. Must be the only format present to_number: the date as a floating point number from_number[:fmt]: format the timestamp using fmt if present otherwise iso

format_date_field(field_name, format_string)

class calibre.utils.formatter_functions.BuiltinFormatDateField[kaynak]

format_date_field(field_name, format_string) – format the value in the field ‘field_name’, which must be the lookup name of date field, either standard or custom. See ‘format_date’ for the formatting codes. This function is much faster than format_date and should be used when you are formatting the value in a field (column). It can’t be used for computed dates or dates in string variables. Example: format_date_field(‘pubdate’, ‘yyyy.MM.dd’)

format_number(v, template)

class calibre.utils.formatter_functions.BuiltinFormatNumber[kaynak]

format_number(v, template) – format the number v using a Python formatting template such as “{0:5.2f}” or “{0:,d}” or “${0:5,.2f}”. The field_name part of the template must be a 0 (zero) (the “{0:” in the above examples). See the template language and Python documentation for more examples. You can leave off the leading “{0:” and trailing “}” if the template contains only a format. Returns the empty string if formatting fails.

human_readable(v)

class calibre.utils.formatter_functions.BuiltinHumanReadable[kaynak]

human_readable(v) – v sayısını KB, MB, GB, vs. olarak temsil eden karakter dizisi döndür

rating_to_stars(value, use_half_stars)

class calibre.utils.formatter_functions.BuiltinRatingToStars[kaynak]

rating_to_stars(value, use_half_stars) – Returns the rating as string of star characters. The value is a number between 0 and 5. Set use_half_stars to 1 if you want half star characters for custom ratings columns that support non-integer ratings, for example 2.5.

urls_from_identifiers(identifiers, sort_results)

class calibre.utils.formatter_functions.BuiltinUrlsFromIdentifiers[kaynak]

urls_from_identifiers(identifiers, sort_results) – given a comma-separated list of identifiers, where an identifier is a colon-separated pair of values (name:id_value), returns a comma-separated list of HTML URLs generated from the identifiers. The list not sorted if sort_results is 0 (character or number), otherwise it is sorted alphabetically by the identifier name. The URLs are generated in the same way as the built-in identifiers column when shown in Book details.

Get values from metadata

annotation_count()

class calibre.utils.formatter_functions.BuiltinAnnotationCount[kaynak]

annotation_count() – return the total number of annotations of all types attached to the current book. This function works only in the GUI.

approximate_formats()

class calibre.utils.formatter_functions.BuiltinApproximateFormats[kaynak]

approximate_formats() – Bir noktada kitaplarla bağlantılı olan virgülle ayrılmış biçimler listesi döndürür. Bu listenin doğru olacağının garantisi yoktur, ama muhtemelen doğrudur. Bu fonksiyon şablon program kipinde “{:’approximate_formats()’}” ile çağrılabilir. Biçim isimlerinin EPUB gibi hep büyük harfli olduğunu unutmayın. Bu fonksiyon yalnızca grafik arayüzde çalışır. Bu değerleri diske-kaydet veya aygıta-gönder şablonlarında kullanmak istiyorsanız özel bir “Diğer sütunlardan oluşturulmuş sütun” yapmalı, fonksiyonu bu sütunun şablonunda, ve bu sütunun değerini kaydet/gönder şablonlarınızda kullanmalısınız

author_links(val_separator, pair_separator)

class calibre.utils.formatter_functions.BuiltinAuthorLinks[kaynak]

author_links(val_separator, pair_separator) – yazarlar listesi ve yazarların bağlantı değerlerini içeren şu biçimde bir karakter dizisi döndürür yazar1 val_separator yazar1link pair_separator yazar2 val_separator yazar2link vs. Yazar, bağlantı değerinden val_separator karakter dizisi ile ek boşluk olmadan ayrılır. yazar:linkvalue çiftleri pair_separator karakter dizisiyle ek boşluk olmadan ayrılır. Yazar isimleri veya bağlantılarında olmayan ayraç karakter dizileri bulmak size kalmıştır. Bir yazar, yazar bağlantısı boş olsa bile dahil edilir.

author_sorts(val_separator)

class calibre.utils.formatter_functions.BuiltinAuthorSorts[kaynak]

author_sorts(val_separator) – kitabın yazarları için yazar sıralama değerleri listesi içeren karakter dizisi döndürür. Sıralama yazar metadata’sında olandır (kitaplardaki author_sort’dan farklı). Döndürülen liste yazar sıra 1 val_separator yazar sıra 2 vs. biçimindedir. Bu listedeki yazar sıralama alanları kitabın yazarlarıyla aynı sıradadır. val_separator arasında boşluk isterseniz ayraç karakter dizisine ekleyebilirsiniz.

booksize()

class calibre.utils.formatter_functions.BuiltinBooksize[kaynak]

booksize() – boyut alanının değerini döndürür. Bu fonksiyon yalnızca grafik arayüzde çalışır. Bu değeri diske-kaydet veya aygıta-gönder şablonlarında kullanmak istiyorsanız, özel bir “Diğer sütunlardan oluşturulmuş sütun” yapmalı, fonksiyonu bu sütunun şablonunda kullanmalısınız, ve bu sütunun değerini kaydet/gönder şablonlarınızda kullanmalısınız

connected_device_name(storage_location)

class calibre.utils.formatter_functions.BuiltinConnectedDeviceName[kaynak]

connected_device_name(storage_location) – if a device is connected then return the device name, otherwise return the empty string. Each storage location on a device can have a different name. The location names are ‘main’, ‘carda’ and ‘cardb’. This function works only in the GUI.

connected_device_uuid(storage_location)

class calibre.utils.formatter_functions.BuiltinConnectedDeviceUUID[kaynak]

connected_device_uuid(storage_location) – if a device is connected then return the device uuid (unique id), otherwise return the empty string. Each storage location on a device has a different uuid. The location names are ‘main’, ‘carda’ and ‘cardb’. This function works only in the GUI.

current_library_name()

class calibre.utils.formatter_functions.BuiltinCurrentLibraryName[kaynak]

current_library_name() – Mevcut Calibre kitaplığına yoldaki son ismi döndürür. Bu fonksiyon şablon program kipinde “{:’current_library_name()’}” ile kullanılabilir.

current_library_path()

class calibre.utils.formatter_functions.BuiltinCurrentLibraryPath[kaynak]

current_library_path() – Mevcut Calibre kitaplığı yolunu döndür. Bu fonksiyon şablon program kipinde “{:’current_library_path()’}” ile kullanılabilir.

current_virtual_library_name()

class calibre.utils.formatter_functions.BuiltinCurrentVirtualLibraryName[kaynak]

current_virtual_library_name() – return the name of the current virtual library if there is one, otherwise the empty string. Library name case is preserved. Example: “program: current_virtual_library_name()”.

field(lookup_name)

class calibre.utils.formatter_functions.BuiltinField[kaynak]

field(lookup_name) – returns the metadata field named by lookup_name

formats_modtimes(date_format)

class calibre.utils.formatter_functions.BuiltinFormatsModtimes[kaynak]

formats_modtimes(date_format) – return a comma-separated list of colon-separated items representing modification times for the formats of a book. The date_format parameter specifies how the date is to be formatted. See the format_date function for details. You can use the select function to get the mod time for a specific format. Note that format names are always uppercase, as in EPUB.

formats_paths()

class calibre.utils.formatter_functions.BuiltinFormatsPaths[kaynak]

formats_paths() – return a comma-separated list of colon-separated items representing full path to the formats of a book. You can use the select function to get the path for a specific format. Note that format names are always uppercase, as in EPUB.

formats_sizes()

class calibre.utils.formatter_functions.BuiltinFormatsSizes[kaynak]

formats_sizes() – return a comma-separated list of colon-separated items representing sizes in bytes of the formats of a book. You can use the select function to get the size for a specific format. Note that format names are always uppercase, as in EPUB.

has_cover()

class calibre.utils.formatter_functions.BuiltinHasCover[kaynak]

has_cover() – kitabın kapağı varsa Yes döndürür, aksi halde boş karakter dizisi döndürür

is_marked()

class calibre.utils.formatter_functions.BuiltinIsMarked[kaynak]

is_marked() – check whether the book is ‘marked’ in calibre. If it is then return the value of the mark, either ‘true’ or the comma-separated list of named marks. Returns ‘’ if the book is not marked.

language_codes(lang_strings)

class calibre.utils.formatter_functions.BuiltinLanguageCodes[kaynak]

language_codes(lang_strings) – lang_strings içinde gönderilen karakter dizileri için dil kodlarını döndür. Karakter dizileri mevcut yerelin dilinde olmalıdır. Lang_strings virgülle ayrılmış listedir.

language_strings(lang_codes, localize)

class calibre.utils.formatter_functions.BuiltinLanguageStrings[kaynak]

language_strings(lang_codes, localize) – lang_codes ile verilen dil kodları için karakter dizisi döndür. localize sıfır ise, karakter dizilerini İngilizce döndürür. Sıfır değilse, karakter dizilerini mevcut yerelin dilinde döndür. Lang_codes virgülle ayrılmış listedir.

ondevice()

class calibre.utils.formatter_functions.BuiltinOndevice[kaynak]

ondevice() – ondevice ayarlanmışsa Yes döndürür. Bu fonksiyon yalnızca grafik arayüzde çalışır. Bu değeri diske-kaydet veya aygıta-gönder şablonlarında kullanmak istiyorsanız, özel bir “Diğer sütunlardan oluşturulmuş sütun” yapmalı, fonksiyonu bu sütunun şablonunda kullanmalısınız, ve bu sütunun değerini kaydet/gönder şablonlarınızda kullanmalısınız

raw_field(lookup_name [, optional_default])

class calibre.utils.formatter_functions.BuiltinRawField[kaynak]

raw_field(lookup_name [, optional_default]) – returns the metadata field named by lookup_name without applying any formatting. It evaluates and returns the optional second argument ‘default’ if the field is undefined (‘None’).

raw_list(lookup_name, separator)

class calibre.utils.formatter_functions.BuiltinRawList[kaynak]

raw_list(lookup_name, separator) – returns the metadata list named by lookup_name without applying any formatting or sorting and with items separated by separator.

series_sort()

class calibre.utils.formatter_functions.BuiltinSeriesSort[kaynak]

series_sort() – seri sıralama değerini döndürür

user_categories()

class calibre.utils.formatter_functions.BuiltinUserCategories[kaynak]

user_categories() – Bu kitabı içeren kullanıcı kategorilerinin virgülle ayrılmış listesini döndürür. Bu fonksiyon yalnızca grafik arayüzde kullanılabilir. Bu değerleri diske-kaydet veya aygıta-gönder şablonlarında kullanmak istiyorsanız önce “Diğer sütunlarda özel oluşturulmuş sütun” yapmalı, fonksiyonu bu sütunun şablonunda kullanmalı, ve bu sütunun değerini kaydet/gönder şablonunuzda kullanmalısınız.

virtual_libraries()

class calibre.utils.formatter_functions.BuiltinVirtualLibraries[kaynak]

virtual_libraries() – return a comma-separated list of Virtual libraries that contain this book. This function works only in the GUI. If you want to use these values in save-to-disk or send-to-device templates then you must make a custom “Column built from other columns”, use the function in that column’s template, and use that column’s value in your save/send templates

If-then-else

check_yes_no(field_name, is_undefined, is_false, is_true)

class calibre.utils.formatter_functions.BuiltinCheckYesNo[kaynak]

check_yes_no(field_name, is_undefined, is_false, is_true) – checks the value of the yes/no field named by the lookup key field_name for a value specified by the parameters, returning “yes” if a match is found, otherwise returning an empty string. Set the parameter is_undefined, is_false, or is_true to 1 (the number) to check that condition, otherwise set it to 0. Example: check_yes_no(“#bool”, 1, 0, 1) returns “yes” if the yes/no field “#bool” is either undefined (neither True nor False) or True. More than one of is_undefined, is_false, or is_true can be set to 1. This function is usually used by the test() or is_empty() functions.

contains(val, pattern, text if match, text if not match)

class calibre.utils.formatter_functions.BuiltinContains[kaynak]

contains(val, pattern, text if match, text if not match) – checks if val contains matches for the regular expression pattern. Returns text if match if matches are found, otherwise it returns text if no match

field_exists(field_name)

class calibre.utils.formatter_functions.BuiltinFieldExists[kaynak]

field_exists(field_name) – checks if a field (column) named field_name exists, returning ‘1’ if so and ‘’ if not.

ifempty(val, boşsa metin)

class calibre.utils.formatter_functions.BuiltinIfempty[kaynak]

ifempty(val, boşsa metin) – val boş değilse val, aksi halde boşsa metin döndürür

test(val, text if not empty, text if empty)

class calibre.utils.formatter_functions.BuiltinTest[kaynak]

test(val, text if not empty, text if empty) – return text if not empty if val is not empty, otherwise return text if empty

Iterating over values

first_non_empty(value [, value]*)

class calibre.utils.formatter_functions.BuiltinFirstNonEmpty[kaynak]

first_non_empty(value [, value]*) – returns the first value that is not empty. If all values are empty, then the empty string is returned. You can have as many values as you want.

lookup(val, [pattern, field,]+ else_field)

class calibre.utils.formatter_functions.BuiltinLookup[kaynak]

lookup(val, [pattern, field,]+ else_field) – like switch, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths

switch(val, [pattern, value,]+ else_value)

class calibre.utils.formatter_functions.BuiltinSwitch[kaynak]

switch(val, [pattern, value,]+ else_value) – for each pattern, value pair, checks if val matches the regular expression pattern and if so, returns that value. If no pattern matches, then else_value is returned. You can have as many pattern, value pairs as you want

switch_if([test_expression, value_expression,]+ else_expression)

class calibre.utils.formatter_functions.BuiltinSwitchIf[kaynak]

switch_if([test_expression, value_expression,]+ else_expression) – for each “test_expression, value_expression” pair, checks if test_expression is True (non-empty) and if so returns the result of value_expression. If no test_expression is True then the result of else_expression is returned. You can have as many “test_expression, value_expression” pairs as you want.

List lookup

identifier_in_list(val, id_name [, found_val, not_found_val])

class calibre.utils.formatter_functions.BuiltinIdentifierInList[kaynak]

identifier_in_list(val, id_name [, found_val, not_found_val]) – treat val as a list of identifiers separated by commas. An identifier has the format “id_name:value”. The id_name parameter is the id_name text to search for, either “id_name” or “id_name:regexp”. The first case matches if there is any identifier matching that id_name. The second case matches if id_name matches an identifier and the regexp matches the identifier’s value. If found_val and not_found_val are provided then if there is a match then return found_val, otherwise return not_found_val. If found_val and not_found_val are not provided then if there is a match then return the identifier:value pair, otherwise the empty string.

in_list(val, separator, [ pattern, found_val, ]+ not_found_val)

class calibre.utils.formatter_functions.BuiltinInList[kaynak]

in_list(val, separator, [ pattern, found_val, ]+ not_found_val) – treating val as a list of items separated by separator, if the pattern matches any of the list values then return found_val.If the pattern matches no list value then return not_found_val. The pattern and found_value pairs can be repeated as many times as desired. The patterns are checked in order. The found_val for the first match is returned. Aliases: in_list(), list_contains()

list_item(val, indis, ayraç)

class calibre.utils.formatter_functions.BuiltinListitem[kaynak]

list_item(val, indis, ayraç) – değeri ayraç ile ayrılmış öğeler listesi olarak yorumla, indis`inci öğeyi döndür. İlk öğe sıfır numaradır. Son öğe `list_item(-1,ayraç) ile döndürülebilir. Öğe listede değilse, boş değer döndürülür. Ayraç count fonksiyonundakiyle aynı anlama sahiptir.

select(val, key)

class calibre.utils.formatter_functions.BuiltinSelect[kaynak]

select(val, key) – interpret the value as a comma-separated list of items, with the items being “id:value”. Find the pair with the id equal to key, and return the corresponding value. Returns the empty string if no match is found.

str_in_list(val, separator, [string, found_val, ]+ not_found_val)

class calibre.utils.formatter_functions.BuiltinStrInList[kaynak]

str_in_list(val, separator, [string, found_val, ]+ not_found_val) – treating val as a list of items separated by separator, if the string matches any of the list values then return found_val.If the string matches no list value then return not_found_val. The comparison is exact match (not contains) and is case insensitive. The string and found_value pairs can be repeated as many times as desired. The patterns are checked in order. The found_val for the first match is returned.

List manipulation

count(val, separator)

class calibre.utils.formatter_functions.BuiltinCount[kaynak]

count(val, separator) – interprets the value as a list of items separated by separator, returning the number of items in the list. Most lists use a comma as the separator, but authors uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}. Aliases: count(), list_count()

list_count_matching(list, pattern, separator)

class calibre.utils.formatter_functions.BuiltinListCountMatching[kaynak]

list_count_matching(list, pattern, separator) – interprets ‘list’ as a list of items separated by ‘separator’, returning the number of items in the list that match the regular expression ‘pattern’. Aliases: list_count_matching(), count_matching()

list_difference(list1, list2, separator)

class calibre.utils.formatter_functions.BuiltinListDifference[kaynak]

list_difference(list1, list2, separator) – return a list made by removing from list1 any item found in list2, using a case-insensitive comparison. The items in list1 and list2 are separated by separator, as are the items in the returned list.

list_equals(list1, sep1, list2, sep2, yes_val, no_val)

class calibre.utils.formatter_functions.BuiltinListEquals[kaynak]

list_equals(list1, sep1, list2, sep2, yes_val, no_val) – return yes_val if list1 and list2 contain the same items, otherwise return no_val. The items are determined by splitting each list using the appropriate separator character (sep1 or sep2). The order of items in the lists is not relevant. The comparison is case insensitive.

list_intersection(list1, list2, separator)

class calibre.utils.formatter_functions.BuiltinListIntersection[kaynak]

list_intersection(list1, list2, separator) – return a list made by removing from list1 any item not found in list2, using a case-insensitive comparison. The items in list1 and list2 are separated by separator, as are the items in the returned list.

list_join(with_separator, list1, separator1 [, list2, separator2]*)

class calibre.utils.formatter_functions.BuiltinListJoin[kaynak]

list_join(with_separator, list1, separator1 [, list2, separator2]*) – return a list made by joining the items in the source lists (list1, etc) using with_separator between the items in the result list. Items in each source list[123…] are separated by the associated separator[123…]. A list can contain zero values. It can be a field like publisher that is single-valued, effectively a one-item list. Duplicates are removed using a case-insensitive comparison. Items are returned in the order they appear in the source lists. If items on lists differ only in letter case then the last is used. All separators can be more than one character. Example:

program:

list_join(‘#@#’, $authors, ‘&’, $tags, ‘,’)

You can use list_join on the results of previous calls to list_join as follows:

program:

a = list_join(‘#@#’, $authors, ‘&’, $tags, ‘,’); b = list_join(‘#@#’, a, ‘#@#’, $#genre, ‘,’, $#people, ‘&’)

You can use expressions to generate a list. For example, assume you want items for authors and #genre, but with the genre changed to the word ‘Genre: ‘ followed by the first letter of the genre, i.e. the genre ‘Fiction’ becomes ‘Genre: F’. The following will do that:

program:

list_join(‘#@#’, $authors, ‘&’, list_re($#genre, ‘,’, ‘^(.).*$’, ‘Genre: 1’), ‘,’)

list_re(src_list, ayraç, include_re, opt_replace)

class calibre.utils.formatter_functions.BuiltinListRe[kaynak]

list_re(src_list, ayraç, include_re, opt_replace) – Ayraç karakteriyle src_list’in öğelere ayrılmasıyla bir liste oluşturulur. Listedeki her öğe için, include_re kuralına uyup uymadığı kontrol edilir. Eşleşiyorsa, döndürülecek listeye eklenir. opt_replace boş karakter dizisi değilse, öğe döndürülecek listeye eklenmeden önce değiştirme yapılır.

list_re_group(src_list, separator, include_re, search_re [, group_template]+)

class calibre.utils.formatter_functions.BuiltinListReGroup[kaynak]

list_re_group(src_list, separator, include_re, search_re [, group_template]+) – Like list_re except replacements are not optional. It uses re_group(list_item, search_re, group_template, …) when doing the replacements on the resulting list.

list_remove_duplicates(list, separator)

class calibre.utils.formatter_functions.BuiltinListRemoveDuplicates[kaynak]

list_remove_duplicates(list, separator) – return a list made by removing duplicate items in the source list. If items differ only in case, the last of them is returned. The items in source list are separated by separator, as are the items in the returned list.

list_sort(list, yön, ayraç)

class calibre.utils.formatter_functions.BuiltinListSort[kaynak]

list_sort(list, yön, ayraç) – büyük küçük harf duyarsız sıralamayla sıralanmış listeyi döndürür. Yön sıfırsa, liste artan sırada, aksi halde azalan sırada sıralanır. list öğeleri ve döndürülen listedeki öğeler ayraç ile ayrılır.

list_split(list_val, sep, id_prefix)

class calibre.utils.formatter_functions.BuiltinListSplit[kaynak]

list_split(list_val, sep, id_prefix) – splits the list_val into separate values using ‘sep’, then assigns the values to variables named ‘id_prefix_N’ where N is the position of the value in the list. The first item has position 0 (zero). The function returns the last element in the list. Example: split(‘one:two:foo’, ‘:’, ‘var’) is equivalent to var_0 = ‘one’; var_1 = ‘two’; var_2 = ‘foo’.

list_union(list1, list2, separator)

class calibre.utils.formatter_functions.BuiltinListUnion[kaynak]

list_union(list1, list2, separator) – return a list made by merging the items in list1 and list2, removing duplicate items using a case-insensitive comparison. If items differ in case, the one in list1 is used. The items in list1 and list2 are separated by separator, as are the items in the returned list. Aliases: list_union(), merge_lists()

range(start, stop, step, limit)

class calibre.utils.formatter_functions.BuiltinRange[kaynak]

range(start, stop, step, limit) – returns a list of numbers generated by looping over the range specified by the parameters start, stop, and step, with a maximum length of limit. The first value produced is ‘start’. Subsequent values next_v are current_v+step. The loop continues while next_v < stop assuming step is positive, otherwise while next_v > stop. An empty list is produced if start fails the test: start>=stop if step is positive. The limit sets the maximum length of the list and has a default of 1000. The parameters start, step, and limit are optional. Calling range() with one argument specifies stop. Two arguments specify start and stop. Three arguments specify start, stop, and step. Four arguments specify start, stop, step and limit. Examples: range(5) -> ‘0,1,2,3,4’. range(0,5) -> ‘0,1,2,3,4’. range(-1,5) -> ‘-1,0,1,2,3,4’. range(1,5) -> ‘1,2,3,4’. range(1,5,2) -> ‘1,3’. range(1,5,2,5) -> ‘1,3’. range(1,5,2,1) -> error(limit exceeded).

subitems(val, başlangıç_indisi, bitiş_indisi)

class calibre.utils.formatter_functions.BuiltinSubitems[kaynak]

subitems(val, başlangıç_indisi, bitiş_indisi) –Bu fonksiyon tarzlar gibi öğe listelerini ayırmak için kullanılır. Değeri virgülle ayrılmış öğeler listesi olarak yorumlar, her öğe noktayla ayrılmış listedir. Önce noktayla ayrılmış öğeleri bulur, sonra her öğe için `başlangıç_indisi`nden `bitiş_indisi`ne bileşenler çıkarılır, sonra sonuçlar bir araya getirilir ve döndürülür. Noktayla ayrılmış listedeki ilk bileşenin indisi sıfırdır. Bir indis eksi değerdeyse, listenin sonundan saymaya başlar. Özel bir durum olarak, bitiş_indisi sıfır olursa listenin boyutu anlamına gelir. Örnek olarak basit şablon kipinde #genre değeri “A.B.C”: {#genre:subitems(0,1)} olursa “A” döner. {#genre:subitems(0,2)} “A.B” döndürür. {#genre:subitems(1,0)} “B.C” döndürür. #genre değerinin “A.B.C, D.E.F” olduğu varsayılırsa, {#genre:subitems(0,1)} “A, D” döndürür. {#genre:subitems(0,2)} “A.B, D.E” döndürür

sublist(val, start_index, end_index, separator)

class calibre.utils.formatter_functions.BuiltinSublist[kaynak]

sublist(val, start_index, end_index, separator) – interpret the value as a list of items separated by separator, returning a new list made from the start_index to the end_index item. The first item is number zero. If an index is negative, then it counts from the end of the list. As a special case, an end_index of zero is assumed to be the length of the list. Examples using basic template mode and assuming that the tags column (which is comma-separated) contains “A, B, C”: {tags:sublist(0,1,\,)} returns “A”. {tags:sublist(-1,0,\,)} returns “C”. {tags:sublist(0,-1,\,)} returns “A, B”.

Other

arguments(id[=expression] [, id[=expression]]*)

class calibre.utils.formatter_functions.BuiltinArguments[kaynak]

arguments(id[=expression] [, id[=expression]]*) – Used in a stored template to retrieve the arguments passed in the call. It both declares and initializes local variables, effectively parameters. The variables are positional; they get the value of the parameter given in the call in the same position. If the corresponding parameter is not provided in the call then arguments assigns that variable the provided default value. If there is no default value then the variable is set to the empty string.

assign(id, val)

class calibre.utils.formatter_functions.BuiltinAssign[kaynak]

assign(id, val) – assigns val to id, then returns val. id must be an identifier, not an expression. This function can often be replaced with the = operator.

globals(id[=expression] [, id[=expression]]*)

class calibre.utils.formatter_functions.BuiltinGlobals[kaynak]

globals(id[=expression] [, id[=expression]]*) – Retrieves “global variables” that can be passed into the formatter. It both declares and initializes local variables with the names of the global variables passed in. If the corresponding variable is not provided in the passed-in globals then it assigns that variable the provided default value. If there is no default value then the variable is set to the empty string.

print(a[, b]*)

class calibre.utils.formatter_functions.BuiltinPrint[kaynak]

print(a[, b]*) – prints the arguments to standard output. Unless you start calibre from the command line (calibre-debug -g), the output will go to a black hole.

Recursion

eval(şablon)

class calibre.utils.formatter_functions.BuiltinEval[kaynak]

eval(şablon) – şablonu, kitap metadata’sı yerine yerel değişkenleri (‘atama’ yapılanlar) vererek değerlendirir. Bu, şablon işlemciyi kullanarak yerel değişkenlerle karmaşık sonuçlar oluşturmaya yarar. { ve } karakterleri özel olduğundan } karakteri yerine ]] ve { karakteri yerine [[ kullanmalısınız; otomatik olarak dönüştürülürler. Ön ek ve son eklerin (|önek|sonek söz dizimi) şablon program kipinde bu fonksiyona bağımsız değişken olarak verileemeyeceğini unutmayın.

template(x)

class calibre.utils.formatter_functions.BuiltinTemplate[kaynak]

template(x) – x’i şablon olarak değerlendirir. Değerlendirme kendi bağlamında yapılır, yani değişkenler çağıran ve şablon değerlendirme tarafından paylaşılmaz. Çünkü { ve } karakterleri özeldir, { karakteri için [[ ve } karakteri için }} kullanmanız gerekir; otomatik olarak dönüştürülürler. Örneğin, template(‘[[title_sort]]’) {title_sort} şablonunu değerlendirir ve değerini döndürür. Ön ek ve son eklerin (|önek|sonek söz dizimi) şablon program kipinde bu fonksiyona bağımsız değişken olarak verilemeyeceğini unutmayın.

Relational

cmp(x, y, lt, eq, gt)

class calibre.utils.formatter_functions.BuiltinCmp[kaynak]

cmp(x, y, lt, eq, gt) – x ve y’yi sayılara dönüştürdükten sonra karşılaştırır. x < y ise lt’yi döndürür. x == y ise eq’yi döndürür. Aksi takdirde gt’yi döndürür. Birçok durumda sayısal karşılaştırma işleçleri (>#, <#, ==# vb.) bu işlevin yerini alabilir.

first_matching_cmp(val, [cmp1, result1,]+, else_result)

class calibre.utils.formatter_functions.BuiltinFirstMatchingCmp[kaynak]

first_matching_cmp(val, [cmp1, result1,]+, else_result) – compares “val < cmpN” in sequence, returning resultN for the first comparison that succeeds. Returns else_result if no comparison succeeds. Example: first_matching_cmp(10,5,”small”,10,”middle”,15,”large”,”giant”) returns “large”. The same example with a first value of 16 returns “giant”.

strcmp(x, y, lt, eq, gt)

class calibre.utils.formatter_functions.BuiltinStrcmp[kaynak]

strcmp(x, y, lt, eq, gt) – does a case-insensitive comparison of x and y as strings. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt. In many cases the lexical comparison operators (>, <, == etc) can replace this function.

strcmpcase(x, y, lt, eq, gt)

class calibre.utils.formatter_functions.BuiltinStrcmpcase[kaynak]

strcmpcase(x, y, lt, eq, gt) – does a case-sensitive comparison of x and y as strings. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt. Note: This is NOT the default behavior used by calibre, for example, in the lexical comparison operators (==, >, <, etc.). This function could cause unexpected results, preferably use strcmp() whenever possible.

String case changes

capitalize(val)

class calibre.utils.formatter_functions.BuiltinCapitalize[kaynak]

capitalize(val) – return val capitalized

lowercase(val)

class calibre.utils.formatter_functions.BuiltinLowercase[kaynak]

lowercase(val) – return val in lower case

titlecase(val)

class calibre.utils.formatter_functions.BuiltinTitlecase[kaynak]

titlecase(val) – return val in title case

uppercase(val)

class calibre.utils.formatter_functions.BuiltinUppercase[kaynak]

uppercase(val) – return val in upper case

String manipulation

character(character_name)

class calibre.utils.formatter_functions.BuiltinCharacter[kaynak]

character(character_name) – returns the character named by character_name. For example, character(‘newline’) returns a newline character (’n’). The supported character names are ‘newline’, ‘return’, ‘tab’, and ‘backslash’.

re(val, pattern, replacement)

class calibre.utils.formatter_functions.BuiltinRe[kaynak]

re(val, pattern, replacement) – return val after applying the regular expression. All instances of pattern are replaced with replacement. As in all of calibre, these are Python-compatible regular expressions

re_group(val, pattern [, template_for_group]*)

class calibre.utils.formatter_functions.BuiltinReGroup[kaynak]

re_group(val, pattern [, template_for_group]*) – return a string made by applying the regular expression pattern to the val and replacing each matched instance with the string computed by replacing each matched group by the value returned by the corresponding template. The original matched value for the group is available as $. In template program mode, like for the template and the eval functions, you use [[ for { and ]] for }. The following example in template program mode looks for series with more than one word and uppercases the first word: {series:’re_group($, “(S* )(.*)”, “[[$:uppercase()]]”, “[[$]]”)’}

shorten(val, left chars, middle text, right chars)

class calibre.utils.formatter_functions.BuiltinShorten[kaynak]

shorten(val, left chars, middle text, right chars) – Return a shortened version of val, consisting of left chars characters from the beginning of val, followed by middle text, followed by right chars characters from the end of the string. Left chars and right chars must be integers. For example, assume the title of the book is Ancient English Laws in the Times of Ivanhoe, and you want it to fit in a space of at most 15 characters. If you use {title:shorten(9,-,5)}, the result will be Ancient E-anhoe. If the field’s length is less than left chars + right chars + the length of middle text, then the field will be used intact. For example, the title The Dome would not be changed.

strcat(a [, b]*)

class calibre.utils.formatter_functions.BuiltinStrcat[kaynak]

strcat(a [, b]*) – can take any number of arguments. Returns the string formed by concatenating all the arguments

strcat_max(max, string1 [, prefix2, string2]*)

class calibre.utils.formatter_functions.BuiltinStrcatMax[kaynak]

strcat_max(max, string1 [, prefix2, string2]*) – Returns a string formed by concatenating the arguments. The returned value is initialized to string1. Prefix, string pairs are added to the end of the value as long as the resulting string length is less than max. String1 is returned even if string1 is longer than max. You can pass as many prefix, string pairs as you wish.

strlen(a)

class calibre.utils.formatter_functions.BuiltinStrlen[kaynak]

strlen(a) – Değişken olarak aldığı karakter dizisinin uzunluğunu döndürür

substr(str, start, end)

class calibre.utils.formatter_functions.BuiltinSubstr[kaynak]

substr(str, start, end) – str’nin start’dan end karakterine kadar aralığını döndürür. str’deki İlk karakter sıfırıncı karakterdir. end eksi bir değerse, sağdak bu kadar karakter anlamına gelir. end sıfırsa, son karakter anlamına gelir. Örneğin, substr(‘12345’, 1, 0) ‘2345’ döndürür, substr(‘12345’, 1, -1) ‘234’ döndürür.

swap_around_articles(val, separator)

class calibre.utils.formatter_functions.BuiltinSwapAroundArticles[kaynak]

swap_around_articles(val, separator) – returns the val with articles moved to the end. The value can be a list, in which case each member of the list is processed. If the value is a list then you must provide the list value separator. If no separator is provided then the value is treated as being a single value, not a list.

swap_around_comma(val)

class calibre.utils.formatter_functions.BuiltinSwapAroundComma[kaynak]

swap_around_comma(val) – “B, A” biçiminde bir değer verildiğinde, “A B” döndür. Bu en çok Soyad Ad şeklindeki isimleri Ad Soyada çevirmede kullanılır. Virgül yoksa, fonksiyon val değerini değiştirmeden döndürür

to_hex(val)

class calibre.utils.formatter_functions.BuiltinToHex[kaynak]

to_hex(val) – hex olarak kodlanmış dizgiyi döndürür. Bu, calibre URL’leri oluştururken kullanışlıdır.

transliterate(a)

class calibre.utils.formatter_functions.BuiltinTransliterate[kaynak]

transliterate(a) – kaynak karakter dizisindeki seslerin yaklaşık halleriyle oluşturulmuş latin alfabesinde bir karakter dizisi döndürür. Örneğin, kaynak “Фёдор Миха́йлович Достоевский” ise fonksiyon “Fiodor Mikhailovich Dostoievskii” döndürür.

Template database functions

book_count(query, use_vl)

class calibre.utils.formatter_functions.BuiltinBookCount[kaynak]

book_count(query, use_vl) – returns the count of books found by searching for query. If use_vl is 0 (zero) then virtual libraries are ignored. This function can be used only in the GUI.

book_values(column, query, sep, use_vl)

class calibre.utils.formatter_functions.BuiltinBookValues[kaynak]

book_values(column, query, sep, use_vl) – returns a list of the values contained in the column “column”, separated by “sep”, in the books found by searching for “query”. If use_vl is 0 (zero) then virtual libraries are ignored. This function can be used only in the GUI.

extra_file_modtime(file_name, format_string)

class calibre.utils.formatter_functions.BuiltinExtraFileModtime[kaynak]

extra_file_modtime(file_name, format_string) – returns the modification time of the extra file ‘file_name’ in the book’s ‘data/’ folder if it exists, otherwise -1.0. The modtime is formatted according to ‘format_string’ (see format_date()). If ‘format_string’ is empty, returns the modtime as the floating point number of seconds since the epoch. The epoch is OS dependent. This function can be used only in the GUI.

extra_file_names(sep [, pattern])

class calibre.utils.formatter_functions.BuiltinExtraFileNames[kaynak]

extra_file_names(sep [, pattern]) – returns a sep-separated list of extra files in the book’s ‘data/’ folder. If the optional parameter ‘pattern’, a regular expression, is supplied then the list is filtered to files that match pattern. The pattern match is case insensitive. This function can be used only in the GUI.

extra_file_size(file_name)

class calibre.utils.formatter_functions.BuiltinExtraFileSize[kaynak]

extra_file_size(file_name) – returns the size in bytes of the extra file ‘file_name’ in the book’s ‘data/’ folder if it exists, otherwise -1.This function can be used only in the GUI.

get_link(field_name, field_value)

class calibre.utils.formatter_functions.BuiltinGetLink[kaynak]

get_link(field_name, field_value) – fetch the link for field ‘field_name’ with value ‘field_value’. If there is no attached link, return ‘’. Example: get_link(‘tags’, ‘Fiction’) returns the link attached to the tag ‘Fiction’.

get_note(field_name, field_value, plain_text)

class calibre.utils.formatter_functions.BuiltinGetNote[kaynak]

get_note(field_name, field_value, plain_text) – fetch the note for field ‘field_name’ with value ‘field_value’. If ‘plain_text’ is empty, return the note’s HTML. If ‘plain_text’ is non-empty, return the note’s plain text. If the note doesn’t exist, return ‘’ in both cases. Example: get_note(‘tags’, ‘Fiction’, ‘’) returns the HTML of the note attached to the tag ‘Fiction’.

has_extra_files([pattern])

class calibre.utils.formatter_functions.BuiltinHasExtraFiles[kaynak]

has_extra_files([pattern]) – returns the count of extra files, otherwise ‘’ (the empty string). If the optional parameter ‘pattern’ (a regular expression) is supplied then the list is filtered to files that match pattern before the files are counted. The pattern match is case insensitive. This function can be used only in the GUI.

has_note(field_name, field_value)

class calibre.utils.formatter_functions.BuiltinHasNote[kaynak]

has_note(field_name, field_value) – return ‘1’ if the value ‘field_value’ in the field ‘field_name’ has an attached note, ‘’ otherwise. Example: has_note(‘tags’, ‘Fiction’) returns ‘1’ if the tag ‘fiction’ has an attached note, ‘’ otherwise.

other

set_globals(id[=expression] [, id[=expression]]*)

class calibre.utils.formatter_functions.BuiltinSetGlobals[kaynak]

set_globals(id[=expression] [, id[=expression]]*) – Sets “global variables” that can be passed into the formatter. The globals are given the name of the id passed in. The value of the id is used unless an expression is provided.

API of the Metadata objects

The python implementation of the template functions is passed in a Metadata object. Knowing it’s API is useful if you want to define your own template functions.

class calibre.ebooks.metadata.book.base.Metadata(title, authors=('Bilinmiyor',), other=None, template_cache=None, formatter=None)[kaynak]

A class representing all the metadata for a book. The various standard metadata fields are available as attributes of this object. You can also stick arbitrary attributes onto this object.

Metadata from custom columns should be accessed via the get() method, passing in the lookup name for the column, for example: “#mytags”.

Use the is_null() method to test if a field is null.

This object also has functions to format fields into strings.

The list of standard metadata fields grows with time is in STANDARD_METADATA_FIELDS.

Please keep the method based API of this class to a minimum. Every method becomes a reserved field name.

is_null(field)[kaynak]

Return True if the value of field is null in this object. ‘null’ means it is unknown or evaluates to False. So a title of _(‘Unknown’) is null or a language of ‘und’ is null.

Be careful with numeric fields since this will return True for zero as well as None.

Also returns True if the field does not exist.

deepcopy(class_generator=<function Metadata.<lambda>>)[kaynak]

Do not use this method unless you know what you are doing, if you want to create a simple clone of this object, use deepcopy_metadata() instead. Class_generator must be a function that returns an instance of Metadata or a subclass of it.

get_identifiers()[kaynak]

Return a copy of the identifiers dictionary. The dict is small, and the penalty for using a reference where a copy is needed is large. Also, we don’t want any manipulations of the returned dict to show up in the book.

set_identifiers(identifiers)[kaynak]

Set all identifiers. Note that if you previously set ISBN, calling this method will delete it.

set_identifier(typ, val)[kaynak]

If val is empty, deletes identifier of type typ

standard_field_keys()[kaynak]

return a list of all possible keys, even if this book doesn’t have them

custom_field_keys()[kaynak]

return a list of the custom fields in this book

all_field_keys()[kaynak]

All field keys known by this instance, even if their value is None

metadata_for_field(key)[kaynak]

return metadata describing a standard or custom field.

all_non_none_fields()[kaynak]

Return a dictionary containing all non-None metadata fields, including the custom ones.

get_standard_metadata(field, make_copy)[kaynak]

return field metadata from the field if it is there. Otherwise return None. field is the key name, not the label. Return a copy if requested, just in case the user wants to change values in the dict.

get_all_standard_metadata(make_copy)[kaynak]

return a dict containing all the standard field metadata associated with the book.

get_all_user_metadata(make_copy)[kaynak]

return a dict containing all the custom field metadata associated with the book.

get_user_metadata(field, make_copy)[kaynak]

return field metadata from the object if it is there. Otherwise return None. field is the key name, not the label. Return a copy if requested, just in case the user wants to change values in the dict.

set_all_user_metadata(metadata)[kaynak]

store custom field metadata into the object. Field is the key name not the label

set_user_metadata(field, metadata)[kaynak]

store custom field metadata for one column into the object. Field is the key name not the label

remove_stale_user_metadata(other_mi)[kaynak]

Remove user metadata keys (custom column keys) if they don’t exist in ‘other_mi’, which must be a metadata object

template_to_attribute(other, ops)[kaynak]

Takes a list [(src,dest), (src,dest)], evaluates the template in the context of other, then copies the result to self[dest]. This is on a best-efforts basis. Some assignments can make no sense.

smart_update(other, replace_metadata=False)[kaynak]

Merge the information in other into self. In case of conflicts, the information in other takes precedence, unless the information in other is NULL.

format_field(key, series_with_index=True)[kaynak]

Returns the tuple (display_name, formatted_value)

to_html()[kaynak]

A HTML representation of this object.

calibre.ebooks.metadata.book.base.STANDARD_METADATA_FIELDS

The set of standard metadata fields.

''' All fields must have a NULL value represented as None for simple types, an empty list/dictionary for complex types and (None, None) for cover_data '''

SOCIAL_METADATA_FIELDS = frozenset(( 'tags', # Ordered list 'rating', # A floating point number between 0 and 10 'comments', # A simple HTML enabled string 'series', # A simple string 'series_index', # A floating point number # Of the form { scheme1:value1, scheme2:value2} # For example: {'isbn':'123456789', 'doi':'xxxx', ... } 'identifiers', ))

''' The list of names that convert to identifiers when in get and set. '''

TOP_LEVEL_IDENTIFIERS = frozenset(( 'isbn', ))

PUBLICATION_METADATA_FIELDS = frozenset(( 'title', # title must never be None. Should be _('Unknown') # Pseudo field that can be set, but if not set is auto generated # from title and languages 'title_sort', 'authors', # Ordered list. Must never be None, can be [_('Unknown')] 'author_sort_map', # Map of sort strings for each author # Pseudo field that can be set, but if not set is auto generated # from authors and languages 'author_sort', 'book_producer', 'timestamp', # Dates and times must be timezone aware 'pubdate', 'last_modified', 'rights', # So far only known publication type is periodical:calibre # If None, means book 'publication_type', 'uuid', # A UUID usually of type 4 'languages', # ordered list of languages in this publication 'publisher', # Simple string, no special semantics # Absolute path to image file encoded in filesystem_encoding 'cover', # Of the form (format, data) where format is, e.g. 'jpeg', 'png', 'gif'... 'cover_data', # Either thumbnail data, or an object with the attribute # image_path which is the path to an image file, encoded # in filesystem_encoding 'thumbnail', ))

BOOK_STRUCTURE_FIELDS = frozenset(( # These are used by code, Null values are None. 'toc', 'spine', 'guide', 'manifest', ))

USER_METADATA_FIELDS = frozenset(( # A dict of dicts similar to field_metadata. Each field description dict # also contains a value field with the key #value#. 'user_metadata', ))

DEVICE_METADATA_FIELDS = frozenset(( 'device_collections', # Ordered list of strings 'lpath', # Unicode, / separated 'size', # In bytes 'mime', # Mimetype of the book file being represented ))

CALIBRE_METADATA_FIELDS = frozenset(( 'application_id', # An application id, currently set to the db_id. 'db_id', # the calibre primary key of the item. 'formats', # list of formats (extensions) for this book # a dict of user category names, where the value is a list of item names # from the book that are in that category 'user_categories', # a dict of items to associated hyperlink 'link_maps', ))

ALL_METADATA_FIELDS = SOCIAL_METADATA_FIELDS.union( PUBLICATION_METADATA_FIELDS).union( BOOK_STRUCTURE_FIELDS).union( USER_METADATA_FIELDS).union( DEVICE_METADATA_FIELDS).union( CALIBRE_METADATA_FIELDS)

# All fields except custom fields STANDARD_METADATA_FIELDS = SOCIAL_METADATA_FIELDS.union( PUBLICATION_METADATA_FIELDS).union( BOOK_STRUCTURE_FIELDS).union( DEVICE_METADATA_FIELDS).union( CALIBRE_METADATA_FIELDS)

# Metadata fields that smart update must do special processing to copy. SC_FIELDS_NOT_COPIED = frozenset(('title', 'title_sort', 'authors', 'author_sort', 'author_sort_map', 'cover_data', 'tags', 'languages', 'identifiers'))

# Metadata fields that smart update should copy only if the source is not None SC_FIELDS_COPY_NOT_NULL = frozenset(('device_collections', 'lpath', 'size', 'comments', 'thumbnail'))

# Metadata fields that smart update should copy without special handling SC_COPYABLE_FIELDS = SOCIAL_METADATA_FIELDS.union( PUBLICATION_METADATA_FIELDS).union( BOOK_STRUCTURE_FIELDS).union( DEVICE_METADATA_FIELDS).union( CALIBRE_METADATA_FIELDS) - \ SC_FIELDS_NOT_COPIED.union( SC_FIELDS_COPY_NOT_NULL)

SERIALIZABLE_FIELDS = SOCIAL_METADATA_FIELDS.union( USER_METADATA_FIELDS).union( PUBLICATION_METADATA_FIELDS).union( CALIBRE_METADATA_FIELDS).union( DEVICE_METADATA_FIELDS) - \ frozenset(('device_collections', 'formats', 'cover_data')) # these are rebuilt when needed

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »

- Calibre’de kurallı ifadelerin kullanımına dair her şey

Calibre’de kurallı ifadelerin kullanımına dair her şey

Kurallı ifadeler, calibrenin birçok yerinde e-kitap içerik ve üstverinin çok yönlü işlemesi için kullanılan bir özellik. Bu öğretici başlangıç, size calibrede kurallı ifadelerin kullanımına başlamanız için yumuşak bir tanıtım.

İçindekiler

- Öncelikle, bir uyarı ve bir kaç cesaret kelimesi

- Calibre’de nerelerde düzenli ifadeler kullanabilirsiniz?

- Yahu nedir bu düzenli ifade?

- Açıklamak istemez misin?

- Çok kötü görünmüyor. Sırada ne var?

- Güzel! Mantıklı gelmeye başladı!

- Bu özel karakterler değişik ve temiz, ama ya bir nokta ya da soru işareti eşleştirmek istersem?

- Peki, en kullanışlı kümeler hangileri?

- Ama bir kaç çeşit karakter dizim varsa, işler karmaşıklaşıyor?

- Şunu atladın…

- Başlangıçta, düzenli ifadeleri büyük küçük harf bağımsız yapabileceğimizi söylemiştin?

- Galiba artık bu düzenli ifadeleri anlamaya başlıyorum… calibre’de bunları nasıl kullanırım?

- Dönüştürmeler

- Kitap Ekleme

- Metadata’nın toplu düzenlenmesi

- Quick reference

- Hazırlayanlar

Öncelikle, bir uyarı ve bir kaç cesaret kelimesi

Bu, kaçınılmaz şekilde bir şekilde teknik olacaktır- neticede, düzenli ifadeler teknik şeyler yapmak için teknik araçlardır. Karmaşık ya da anlaşılmaz görünebilecek bazı kavramlar veya jargon kullanmam gerekecek. Bu kavramları olabildiğince basit anlatmaya çalışacağım, ama hiç anlatmadan da yapamam. Bunu söyledikten sonra, gördüğünüz jargonlar cesaretinizi kırmasın, herşeyi baştan anlatmaya çalışıyorum. Ve düzenli ifadeler kara büyü ya da gizli saklı şeyler gibi görünseler de (ya da rastgele karakter dizileri ve işaretler gibi), bu kadar karmaşık olmadıkları konusunda sözüme güvenebilirsiniz. Düzenli ifadeleri gerçekten anlayan insanlar bile daha karmaşık olanları okumakta zorlanabilirler, ama yazması o kadar da zor değildir- ifadeyi adım adım oluşturursunuz. Şimdi bir adım atın ve beni tavşan deliğine doğru takip edin.

Calibre’de nerelerde düzenli ifadeler kullanabilirsiniz?

There are a few places calibre uses regular expressions. There’s the Search & replace in conversion options, metadata detection from filenames in the import settings and Search & replace when editing the metadata of books in bulk. The calibre book editor can also use regular expressions in its Search and replace feature. Finally, you can use regular expressions when searching the calibre book list and when searching inside the calibre E-book viewer.

Yahu nedir bu düzenli ifade?

Bir düzenli ifade karakter dizisi kümelerini tanımlama yöntemidir. Tek bir düzenli ifade değişik belirli sayıda karakter dizisiyle eşleşebilir. Düzenli ifadeyi bu kadar güçlü kılan budur – çok sayıda çeşitlemeyi ifade edecek öz bir yöntemdir.

Not

I’m using string here in the sense it is used in programming languages: a string of one or more characters, characters including actual characters, numbers, punctuation and so-called whitespace (linebreaks, tabulators etc.). Please note that generally, uppercase and lowercase characters are not considered the same, thus “a” being a different character from “A” and so forth. In calibre, regular expressions are case insensitive in the Search bar, but not in the conversion options. There’s a way to make every regular expression case insensitive, but we’ll discuss that later. It gets complicated because regular expressions allow for variations in the strings it matches, so one expression can match multiple strings, which is why people bother using them at all. More on that in a bit.

Açıklamak istemez misin?

Peki, zaten bu yüzden burdayız. Öncelikle, düzenli ifadelerdeki en önemli kavram şudur: Bir karakter dizisi kendisiyle eşleşen bir düzenli ifadedir. Yani, "Hello, World!" karakter dizisiyle eşleşen bir düzenli ifade istesem, kullanmam gereken düzenli ifade Hello, World!``olurdu. Ve evet, gerçekten bu kadar basit. Fark edeceğiniz üzere, bu *yalnızca* bire bir karakter dizisiyle eşleşir yani ``"Hello, World!" ile, "Hello, wOrld!" veya "hello, world!" ile ya da başka bir çeşidiyle değil.

Çok kötü görünmüyor. Sırada ne var?

Bir sonraki iyi öğelerin başlangıcı olacak. Kurallı ifadelerin, çoklu dizelerle eşleşebileceğini söylediğimi hatırlıyor musunuz? Bu noktada iş biraz karmaşık hal alıyor. Diyelim ki, nispeten daha bir pratik çalışma olarak,

Güzel! Mantıklı gelmeye başladı!

I was hoping you’d say that. But brace yourself, now it gets even better! We just saw that using sets, we could match one of several characters at once. But you can even repeat a character or set, reducing the number of expressions needed to handle the above page number example to one. Yes, ONE! Excited? You should be! It works like this: Some so-called special characters, “+”, “?” and “*”, repeat the single element preceding them. (Element means either a single character, a character set, an escape sequence or a group (we’ll learn about those last two later)- in short, any single entity in a regular expression). These characters are called wildcards or quantifiers. To be more precise, “?” matches 0 or 1 of the preceding element, “*” matches 0 or more of the preceding element and “+” matches 1 or more of the preceding element. A few examples: The expression a? would match either “” (which is the empty string, not strictly useful in this case) or “a”, the expression a* would match “”, “a”, “aa” or any number of a’s in a row, and, finally, the expression a+ would match “a”, “aa” or any number of a’s in a row (Note: it wouldn’t match the empty string!). Same deal for sets: The expression [0-9]+ would match every integer number there is! I know what you’re thinking, and you’re right: If you use that in the above case of matching page numbers, wouldn’t that be the single one expression to match all the page numbers? Yes, the expression Page [0-9]+ of 423 would match every page number in that book!

Not

A note on these quantifiers: They generally try to match as much text as possible, so be careful when using them. This is called “greedy behaviour”- I’m sure you get why. It gets problematic when you, say, try to match a tag. Consider, for example, the string "<p class="calibre2">Title here</p>" and let’s say you’d want to match the opening tag (the part between the first pair of angle brackets, a little more on tags later). You’d think that the expression <p.*> would match that tag, but actually, it matches the whole string! (The character “.” is another special character. It matches anything except linebreaks, so, basically, the expression .* would match any single line you can think of). Instead, try using <p.*?> which makes the quantifier "*" non-greedy. That expression would only match the first opening tag, as intended. There’s actually another way to accomplish this: The expression <p[^>]*> will match that same opening tag- you’ll see why after the next section. Just note that there quite frequently is more than one way to write a regular expression.

Bu özel karakterler değişik ve temiz, ama ya bir nokta ya da soru işareti eşleştirmek istersem?

Bunu da tabi ki yapabilirsiniz: Herhangi özel karakterin önüne ters bölü işareti koyarsanız, bu karakter herhangi bir özel anlamı olmadan neyse o olarak yorumlanır. Bir ters bölü işareti çifti ve ardından gelen tek bir karaktere kaçış düzeni denir, ve özel bir karakterin önüne ters bölü koymaya da karakteri kaçırmak denir. Kaçış düzeni tek bir öğe gibi yorumlanır. Tabi ki yalnızca özel karakterleri kaçırmaktan fazlasınnı yapan kaçış düzenleri de vardır, örneğin "\t" bir sekme anlamına gelir. Bazı kaçış düzenlerinden daha sonra bahsedeceğiz. Ve bu arada, bu özel karakterlerle ilgili olarak: Bu girişte bahsi geçen karakterleri bir fonksiyona sahipmiş ve karakterin aslını kullanmak için kaçırmanız gerekiyormuş gibi düşünebilirsiniz.

Peki, en kullanışlı kümeler hangileri?

Knew you’d ask. Some useful sets are [0-9] matching a single number, [a-z] matching a single lowercase letter, [A-Z] matching a single uppercase letter, [a-zA-Z] matching a single letter and [a-zA-Z0-9] matching a single letter or number. You can also use an escape sequence as shorthand:

\d

is equivalent to [0-9]

\w

is equivalent to [a-zA-Z0-9_]

\s

is equivalent to any whitespace

Not

“Whitespace” is a term for anything that won’t be printed. These characters include space, tabulator, line feed, form feed, carriage return, non-breaking spaces, etc.

Not

The upper and lower case sets may match both upper and lowercase if the setting to make searches case insensitive is enabled. Such settings are found, for instance in Preferences->Searching in calibre itself and on the Search panel in the calibre E-book viewer as well as the calibre Edit book tool.

Kümelerle ilgili son bir not olarak, bir kümeyi herhangi bir karakter gibi ama kümedeki karakterler gibi tanımlayabileceğinizi söyleyelim. Bunu "^" karakterini kümedeki ilk karakter olarak ekleyerek yapabilirsiniz. Böylece, [^a] “a” olmayan herhangi bir karakterle eşleşir. Buna kümeyi tümleme denir. Daha önce gördüğümüz kaçış düzeni kısayolları de tümlenebilirler: "\D" sayı olmayan herhangi bir karakter demektir, yani [^0-9] ile eşittir. Diğer kısayollar da tahmin edeceğiniz üzere, küçük harf olan yerine büyük harf kullanarak tümlenebilir. Önceki kısımdan <p[^>]*> örneğine gidersek, kullandığı karakter kümesinin kapanan bir açı işareti dışındaki herhangi bir karakterle eşleştiğini görebilirsiniz.

Ama bir kaç çeşit karakter dizim varsa, işler karmaşıklaşıyor?

Fear not, life still is good and easy. Consider this example: The book you’re converting has “Title” written on every odd page and “Author” written on every even page. Looks great in print, right? But in e-books, it’s annoying. You can group whole expressions in normal parentheses, and the character "|" will let you match either the expression to its right or the one to its left. Combine those and you’re done. Too fast for you? Okay, first off, we group the expressions for odd and even pages, thus getting (Title)(Author) as our two needed expressions. Now we make things simpler by using the vertical bar ("|" is called the vertical bar character): If you use the expression (Title|Author) you’ll either get a match for “Title” (on the odd pages) or you’d match “Author” (on the even pages). Well, wasn’t that easy?

Tabi ki, grup parantezleri kullanmadan da dik çubuğu kullanabilirsiniz. Nicelik sözcüklerinin önlerindeki öğeleri tekrarladıklarını söylediğimi hatırlıyorsunuz değil mi? Dikey çubuk biraz daha değişik çalışır: “Title|Author” ifadesi de ya “Title” ya da “Author” karakter dizisiyle eşleşir, yukardaki örnekteki gibi gruplamayla. Dikey çubuk tüm ifade arasından önce geleni seçer ve takip eder. Yani, “Calibre” ve “calibre” karakter dizileriyle eşleşme istediğinizde ve yalnızca büyük harf- ve küçük harf “c” arasını seçmek istediğinizde , (c|C)alibre ifadesini kullanmanız gerekir, gruplama “c” nin seçildiğinden emin olmanızı sağlar. c|Calibre kullansaydınız, “c” karakterinde eşleşme alırdınız ya da “Calibre” karakter dizisinde, ki burada istediğimiz bu değil. Kısaca: şüphe duyarsanız, gruplamayı dikey çubukla kullanın.

Şunu atladın…

…bir dakika, gruplarla yapabileceğiniz bir tane son, gerçekten hoş şey var. Daha önce eşleşen bir grubunuz varsa, sonraki ifadelerde bu gruba başvurabilirsiniz: Gruplar 1 ile başlayarak numaralandırılır, ve başvurmak için grup numarasını kaçırmanız gerekir, yani beşinci gruba \5 ile başvurulur. “Test Test” karakter dizisinde ([^ ]+) \1 aradıysanız, tüm karakter dizisiyle eşleşirsiniz!

Başlangıçta, düzenli ifadeleri büyük küçük harf bağımsız yapabileceğimizi söylemiştin?

Yes, I did, thanks for paying attention and reminding me. You can tell calibre how you want certain things handled by using something called flags. You include flags in your expression by using the special construct (?flags go here) where, obviously, you’d replace “flags go here” with the specific flags you want. For ignoring case, the flag is i, thus you include (?i) in your expression. Thus, (?i)test would match “Test”, “tEst”, “TEst” and any case variation you could think of.

Another useful flag lets the dot match any character at all, including the newline, the flag s. If you want to use multiple flags in an expression, just put them in the same statement: (?is) would ignore case and make the dot match all. It doesn’t matter which flag you state first, (?si) would be equivalent to the above.

Galiba artık bu düzenli ifadeleri anlamaya başlıyorum… calibre’de bunları nasıl kullanırım?

Dönüştürmeler

Let’s begin with the conversion settings, which is really neat. In the Search & replace part, you can input a regexp (short for regular expression) that describes the string that will be replaced during the conversion. The neat part is the wizard. Click on the wizard staff and you get a preview of what calibre “sees” during the conversion process. Scroll down to the string you want to remove, select and copy it, paste it into the regexp field on top of the window. If there are variable parts, like page numbers or so, use sets and quantifiers to cover those, and while you’re at it, remember to escape special characters, if there are some. Hit the button labeled Test and calibre highlights the parts it would replace were you to use the regexp. Once you’re satisfied, hit OK and convert. Be careful if your conversion source has tags like this example:

Maybe, but the cops feel like you do, Anita. What's one more dead vampire?
New laws don't change that. </p>
<p class="calibre4"> <b class="calibre2">Generated by ABC Amber LIT Conv
<a href="http://www.processtext.com/abclit.html" class="calibre3">erter,
http://www.processtext.com/abclit.html</a></b></p>
<p class="calibre4"> It had only been two years since Addison v. Clark.
The court case gave us a revised version of what life was

(shamelessly ripped out of this thread [https://www.mobileread.com/forums/showthread.php?t=75594"]). You’d have to remove some of the tags as well. In this example, I’d recommend beginning with the tag <b class="calibre2">, now you have to end with the corresponding closing tag (opening tags are <tag>, closing tags are </tag>), which is simply the next </b> in this case. (Refer to a good HTML manual or ask in the forum if you are unclear on this point). The opening tag can be described using <b.*?>, the closing tag using </b>, thus we could remove everything between those tags using <b.*?>.*?</b>. But using this expression would be a bad idea, because it removes everything enclosed by <b>- tags (which, by the way, render the enclosed text in bold print), and it’s a fair bet that we’ll remove portions of the book in this way. Instead, include the beginning of the enclosed string as well, making the regular expression <b.*?>\s*Generated\s+by\s+ABC\s+Amber\s+LIT.*?</b> The \s with quantifiers are included here instead of explicitly using the spaces as seen in the string to catch any variations of the string that might occur. Remember to check what calibre will remove to make sure you don’t remove any portions you want to keep if you test a new expression. If you only check one occurrence, you might miss a mismatch somewhere else in the text. Also note that should you accidentally remove more or fewer tags than you actually wanted to, calibre tries to repair the damaged code after doing the removal.

Kitap Ekleme

Düzenli ifadelerin kullanılabileceği başka bir yer de dosya isimlerinden metadata çıkarmaktır. Bu özelliği ayarların “Kitap ekleme” kısmında bulabilirsiniz. Burada özel bir özellik vardır: Metadata alanları için alan isimlerini kullanabilirsiniz, örneğin (?P<title>) calibre’nin kitap başlığı olarak karakter dizisinin bu kısmını kullanacağı anlamına gelir. İzin verilen alan isimleri güzel bir test alanıyla beraber pencerelerde listelenir. Örnek: Klasik Metinler: The Divine Comedy by Dante Alighieri.mobi (Klasik İtalyan şiirini hepimiz sevdiğimizden, bu tabi ki zaten kitaplığınızda) veya Bilim Kurgu harikaları: The Foundation Trilogy by Isaac Asimov.epub gibi bir takım dosyaları içe aktarmak istediğinizi varsayalım. Bu kesinlikle calibre’nin anlamlı bir veri çıkaramayacağı bir isimlendirme şeması - çünkü metadata çıkarmak için kullandığı standart ifade (?P<title>.+) - (?P<author>[^_]+). Burada çalışacak bir düzenli ifade [a-zA-Z]+: (?P<title>.+) by (?P<author>.+) olurdu. Metadata alanı için olan grup içinde, alanın gerçekten neyle eşleşeceğini tanımlayan ifadeler kullanmanız gerektiğini de not alın lütfen. Ayrıca calibre’nin sağladığı test alanını kullanırken test dosya isminize dosya uzantısını da girmelisiniz, aksi halde çalışan bir ifadeniz olsa da hiçbir eşleşme almazsınız.

Metadata’nın toplu düzenlenmesi

The last part is regular expression Search and replace in metadata fields. You can access this by selecting multiple books in the library and using bulk metadata edit. Be very careful when using this last feature, as it can do Very Bad Things to your library! Doublecheck that your expressions do what you want them to using the test fields, and only mark the books you really want to change! In the regular expression search mode, you can search in one field, replace the text with something and even write the result into another field. A practical example: Say your library contained the books of Frank Herbert’s Dune series, named after the fashion Dune 1 - Dune, Dune 2 - Dune Messiah and so on. Now you want to get Dune into the series field. You can do that by searching for (.*?) \d+ - .* in the title field and replacing it with \1 in the series field. See what I did there? That’s a reference to the first group you’re replacing the series field with. Now that you have the series all set, you only need to do another search for .*? - in the title field and replace it with "" (an empty string), again in the title field, and your metadata is all neat and tidy. Isn’t that great? By the way, instead of replacing the entire field, you can also append or prepend to the field, so, if you wanted the book title to be prepended with series info, you could do that as well. As you by now have undoubtedly noticed, there’s a checkbox labeled Case sensitive, so you won’t have to use flags to select behaviour here.

Well, that just about concludes the very short introduction to regular expressions. Hopefully I’ll have shown you enough to at least get you started and to enable you to continue learning by yourself- a good starting point would be the Python documentation for regexps [https://docs.python.org/library/re.html].

One last word of warning, though: Regexps are powerful, but also really easy to get wrong. calibre provides really great testing possibilities to see if your expressions behave as you expect them to. Use them. Try not to shoot yourself in the foot. (God, I love that expression…). But should you, despite the warning, injure your foot (or any other body parts), try to learn from it.

Quick reference

- Quick reference for regexp syntax

- Character classes
- Shorthand character classes
- The quantifiers
- Greed
- Alternation
- Exclusion
- Anchors
- Groups
- Lookarounds
- Recursion
- Special characters
- Meta-characters
- Modes

Hazırlayanlar

İpuçları, düzeltmeler vs. için teşekkürler:

- ldolse

- kovidgoyal

- chaley

- dwanthny

- kacir

- Starson17

- Orpheu

For more about regexps see The Python User Manual [https://docs.python.org/library/re.html]. The actual regular expression library used by calibre is: regex [https://bitbucket.org/mrabarnett/mrab-regex/src/hg/] which supports several useful enhancements over the Python standard library one.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »

- Calibre’de kurallı ifadelerin kullanımına dair her şey »

- Quick reference for regexp syntax

Quick reference for regexp syntax

This checklist summarizes the most commonly used/hard to remember parts of the regexp engine available in most parts of calibre.

İçindekiler

- Character classes

- Shorthand character classes

- The quantifiers

- Greed

- Alternation

- Exclusion

- Anchors

- Groups

- Lookarounds

- Recursion

- Special characters

- Meta-characters

- Modes

Character classes

Character classes are useful to represent different groups of characters, succinctly.

Examples:

Representation

Class

[a-z]

Lowercase letters. Does not include characters with accent mark and ligatures

[a-z0-9]

Lowercase letters from a to z or numbers from 0 to 9

[A-Za-z-]

Uppercase or lowercase letters, or a dash. To include the dash in a class, you must put it at the beginning or at the end so as not to confuse it with the hyphen that specifies a range of characters

[^0-9]

Any character except a digit. The caret (^) placed at the beginning of the class excludes the characters of the class (complemented class)

[[a-z]--[aeiouy]]

The lowercase consonants. A class can be included in a class. The characters -- exclude what follows them

[\w--[\d_]]

All letters (including foreign accented characters). Abbreviated classes can be used inside a class

Example:

<[^<>]+> to select an HTML tag

Shorthand character classes

Representation

Class

\d

A digit (same as [0-9])

\D

Any non-numeric character (same as [^0-9])

\w

An alphanumeric character ([a-zA-Z0-9]) including characters with accent mark and ligatures

\W

Any “non-word” character

\s

Space, non-breaking space, tab, return line

\S

Any “non-whitespace” character

.

Any character except newline. Use the “dot all” checkbox or the (?s) regexp modifier to include the newline character.

The quantifiers

Quantifier

Number of occurrences of the expression preceding the quantifier

?

0 or 1 occurrence of the expression. Same as {0,1}

+

1 or more occurrences of the expression. Same as {1,}

*

0, 1 or more occurrences of the expression. Same as {0,}

{n}

Exactly n occurrences of the expression

{min,max}

Number of occurrences between the minimum and maximum values included

{min,}

Number of occurrences between the minimum value included and the infinite

{,max}

Number of occurrences between 0 and the maximum value included

Greed

By default, with quantifiers, the regular expression engine is greedy: it extends the selection as much as possible. This often causes surprises, at first. ? follows a quantifier to make it lazy. Avoid putting two in the same expression, the result can be unpredictable.

Beware of nesting quantifiers, for example, the pattern (a*)*, as it exponentially increases processing time.

Alternation

The | character in a regular expression is a logical OR. It means that either the preceding or the following expression can match.

Exclusion

Method 1
pattern_to_exclude(*SKIP)(*FAIL)|pattern_to_select

Example:

"Blabla"(*SKIP)(*FAIL)|Blabla
selects Blabla, in the strings Blabla or “Blabla or Blabla”, but not in “Blabla”.

Method 2
pattern_to_exclude\K|(pattern_to_select)

"Blabla"\K|(Blabla)

selects Blabla, in the strings Blabla or “Blabla or Blabla”, but not in “Blabla”.

Anchors

An anchor is a way to match a logical location in a string, rather than a character. The most useful anchors for text processing are:

\b

Designates a word boundary, i.e. a transition from space to non-space character. For example, you can use \bsurd to match the surd but not absurd.

^

Matches the start of a line (in multi-line mode, which is the default)

$

Matches the end of a line (in multi-line mode, which is the default)

\K

Resets the start position of the selection to its position in the pattern. Some regexp engines (but not calibre) do not allow lookbehind of variable length, especially with quantifiers. When you can use \K with these engines, it also allows you to get rid of this limit by writing the equivalent of a positive lookbehind of variable length.

Groups

(expression)

Capturing group, which stores the selection and can be recalled later in the search or replace patterns with \n, where n is the sequence number of the capturing group (starting at 1 in reading order)

(?:expression)

Group that does not capture the selection

(?>expression)

Atomic Group: As soon as the expression is satisfied, the regexp engine passes, and if the rest of the pattern fails, it will not backtrack to try other combinations with the expression. Atomic groups do not capture.

(?|expression)

Branch reset group: the branches of the alternations included in the expression share the same group numbers

(?<name>expression)

Group named “name”. The selection can be recalled later in the search pattern by (?P=name) and in the replace by \g<name>. Two different groups can use the same name.

Lookarounds

Lookaround

Meaning

?=

Positive lookahead (to be placed after the selection)

?!

Negative lookahead (to be placed after the selection)

?<=

Positive lookbehind (to be placed before the selection)

?<!

Negative lookbehind (to be placed before the selection)

Lookaheads and lookbehinds do not consume characters, they are zero length and do not capture. They are atomic groups: as soon as the assertion is satisfied, the regexp engine passes, and if the rest of the pattern fails, it will not backtrack inside the lookaround to try other combinations.

When looking for multiple matches in a string, at the starting position of each match attempt, a lookbehind can inspect the characters before the current position. Therefore, on the string 123, the pattern (?<=\d)\d (a digit preceded by a digit) should, in theory, select 2 and 3. On the other hand, \d\K\d can only select 2, because the starting position after the first selection is immediately before 3, and there are not enough digits for a second match. Similarly, \d(\d) only captures 2. In calibre’s regexp engine practice, the positive lookbehind behaves in the same way, and selects only 2, contrary to theory.

Groups can be placed inside lookarounds, but capture is rarely useful. Nevertheless, if it is useful, it will be necessary to be very careful in the use of a quantifier in a lookbehind: the greed associated with the absence of backtracking can give a surprising capture. For this reason, use \K rather than a positive lookbehind when you have a quantifier (or worse, several) in a capturing group of the positive lookbehind.

Example of negative lookahead:

(?![^<>{}]*[>}])

Placed at the end of the pattern prevents to select within a tag or a style embedded in the file.

Whenever possible, it is always better to “anchor” the lookarounds, to reduce the number of steps necessary to obtain the result.

Recursion

Representation

Meaning

(?R)

Recursion of the entire pattern

(?1)

Recursion of the only pattern of the numbered capturing group, here group 1

Recursion is calling oneself. This is useful for balanced queries, such as quoted strings, which can contain embedded quoted strings. Thus, if during the processing of a string between double quotation marks, we encounter the beginning of a new string between double quotation marks, well we know how to do, and we call ourselves. Then we have a pattern like:

start-pattern(?>atomic sub-pattern|(?R))*end-pattern

To select a string between double quotation marks without stopping on an embedded string:

“((?>[^“”]+|(?R))*[^“”]+)”

This template can also be used to modify pairs of tags that can be embedded, such as <div> tags.

Special characters

Representation

Character

\t

tabulation

\n

line break

\x20

(breakable) space

\xa0

no-break space

Meta-characters

Meta-characters are those that have a special meaning for the regexp engine. Of these, twelve must be preceded by an escape character, the backslash (\), to lose their special meaning and become a regular character again:

^ . [ ] $ ( ) * + ? | \

Seven other meta-characters do not need to be preceded by a backslash (but can be without any other consequence):

{ } ! < > = :

Special characters lose their status if they are used inside a class (between brackets []). The closing bracket and the dash have a special status in a class. Outside the class, the dash is a simple literal, the closing bracket remains a meta-character.

The slash (/) and the number sign (or hash character) (#) are not meta-characters, they don’t need to be escaped.

In some tools, like regex101.com with the Python engine, double quotes have the special status of separator, and must be escaped, or the options changed. This is not the case in the editor of calibre.

Modes

(?s)

Causes the dot (.) to match newline characters as well

(?m)

Makes the ^ and $ anchors match the start and end of lines instead of the start and end of the entire string.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »

- Calibre’nin işlevselliğini artırmak için kendi eklentilerinizi yazmak

Calibre’nin işlevselliğini artırmak için kendi eklentilerinizi yazmak
calibre oldukça modüler bir tasarıma sahiptir. Calibre’deki hemen hemen tüm işlevler eklentiler biçiminde gelir. Eklentiler dönüşüm için, haber indirmek için (bunlara tarif denir), kullanıcı arayüzünün çeşitli bileşenleri için, farklı cihazlara bağlanmak için, dosyaları calibre’ye eklerken işlemek için kullanılır. Tercihler → Gelişmiş → Eklentiler’e giderek calibre’deki tüm yerleşik eklentilerin tam listesini alabilirsiniz.

Burda, calibre’ye yeni işlevler kazandırmak için kendi eklentilerinizi nasıl oluşturacağınızı öğreteceğiz.

İçindekiler

- Bir calibre eklentisinin anatomisi

- Kullanıcı Arayüzü eklentisi

- __init__.py

- ui.py

- main.py

- Eklenti ZIP dosyasından kaynak alma

- Eklentiniz için kullanıcı yapılandırmasını etkinleştirmek

- Kitap eklentilerini düzenle

- main.py

- Eklentinize çeviriler eklemek

- Eklenti API’si

- Eklentilerde hata ayıklama

- Daha fazla eklenti örneği

- Eklentilerinizi başkalarıyla paylaşmak

Not

Bu yalnızca >= 0.8.60 calibre sürümleri ile mümkündür

Bir calibre eklentisinin anatomisi

Calibre eklentisi çok basittir, sadece bazı Python kodlarını ve eklentinin ihtiyaç duyduğu görüntü dosyaları gibi diğer kaynakları içeren bir ZIP dosyasıdır. Lafı fazla uzatmadan basit bir örneğe bakalım.

Çeşitli e-belgeleri EPUB ve MOBI biçimlerinde dağıtmak için kullandığınız bir calibre kurulumunuz olduğunu varsayın. Calibre tarafından üretilen tüm dosyaların yayıncısının “Merhaba dünya” olmasını istersiniz, bunu şu şekilde yapabilirsiniz. __init__.py isimli bir dosya oluşturun (bu özel bir isimdir ve eklentinizin ana dosyası için her zaman kullanılmak zorundadır) ve aşağıdaki Python kodunu girin:

from calibre.customize import FileTypePlugin

class HelloWorld(FileTypePlugin):

name = 'Hello World Plugin' # Name of the plugin
description = 'Set the publisher to Hello World for all new conversions'
supported_platforms = ['windows', 'osx', 'linux'] # Platforms this plugin will run on
author = 'Acme Inc.' # The author of this plugin
version = (1, 0, 0) # The version number of this plugin
file_types = {'epub', 'mobi'} # The file types that this plugin will be applied to
on_postprocess = True # Run this plugin after conversion is complete
minimum_calibre_version = (0, 7, 53)

def run(self, path_to_ebook):
from calibre.ebooks.metadata.meta import get_metadata, set_metadata
with open(path_to_ebook, 'r+b') as file:
ext = os.path.splitext(path_to_ebook)[-1][1:].lower()
mi = get_metadata(file, ext)
mi.publisher = 'Hello World'
set_metadata(file, mi, ext)
return path_to_ebook

Bu kadar. Bu kodu calibre’ye eklenti olarak eklemek için, oluşturduğunuz klasörde aşağıdakini çalıştırmanız yeterlidir __init__.py:

calibre-customize -b .

Not

MacOS’ta komut satırı araçları calibre paketinin içindedir; örneğin, calibre’yi /Applications dizinine yüklediyseniz komut satırı araçları /Applications/calibre.app/Contents/MacOS/ konumundadır .

Merhaba Dünya eklentisini helloworld_plugin.zip [https://calibre-ebook.com/downloads/helloworld_plugin.zip] adresinden indirebilirsiniz.

Calibre kullanarak her bir kitap dönüştürme işlemi yaptığınızda, eklentinin run() metodu çağrılacak ve dönüştürülen kitabın yayımcısı olarak “Merhaba Dünya” ayarlanacak. Bu önemsiz bir eklenti, daha karmaşık ve kullanıcı arayüzüne bir şeyler ekleyen bir tanesine geçelim.

Kullanıcı Arayüzü eklentisi

Bu eklenti birkaç dosyaya yayılacaktır (kodu temiz tutmak için). Eklenti ZIP dosyasından kaynakları (görüntüler veya veri dosyaları) nasıl alacağınızı, kullanıcıların eklentinizi yapılandırmasına olanak tanıyacağını, calibre kullanıcı arayüzünde nasıl öğeler oluşturacağınızı ve calibre’de kitap veritabanına nasıl erişip sorgulayacağınızı size gösterecektir.

Bu eklentiyi interface_demo_plugin.zip [https://calibre-ebook.com/downloads/interface_demo_plugin.zip] adresinden indirebilirsiniz.

Unutulmaması gereken ilk şey, bu ZIP dosyasının içinde çok daha fazla dosya bulunmasıdır, aşağıda açıklandığı gibi, ``eklenti-içe aktarma-adı-arabirimi_demo.txt``ye özellikle dikkat edin.

plugin-import-name-interface_demo.txt

Çoklu dosya eklentisi büyüsünü etkinleştirmek için kullanılan boş bir metin dosyası. Bu dosya, birden fazla .py dosyası kullanan tüm eklentilerde bulunmalıdır. Boş olmalı ve dosya adı şu biçimde olmalıdır: eklenti-içe aktarma-adı-**bir_adı**.txt. Bu dosyanın varlığı, aşağıdaki gibi bir ifade kullanarak ZIP dosyasında bulunan .py dosyalarından kod içe aktarmanıza olanak tanır:

from calibre_plugins.some_name.some_module import some_object

calibre_plugins öneki her zaman mevcut olmalıdır. bir_ad boş metin dosyasının dosya adından gelir. some_module, ZIP dosyasındaki some_module.py dosyasını ifade eder. Bu içe aktarmanın normal Python içe aktarmaları kadar güçlü olduğunu unutmayın. Tıpkı normalde yaptığınız gibi (her alt klasörde __init__.py tanımlayarak) ZIP dosyası içinde .py modüllerinin paketlerini ve alt paketlerini oluşturabilirsiniz ve her şey “çalışır”.

bir_ad için kullandığınız ad, tüm eklentiler tarafından paylaşılan genel bir ad alanına girer, bu yüzden onu mümkün olduğunca benzersiz yapın. Ancak bunun geçerli bir Python tanımlayıcısı olması gerektiğini unutmayın (yalnızca harfler, sayılar ve alt çizgi).

__init__.py

Daha önce olduğu gibi, eklenti sınıfını belirleyen dosya

main.py

Bu dosya işe yarar bir şeyler yapan gerçek kodu içerir

ui.py

Bu dosya eklentinin arayüz kısmını tanımlar

images/icon.png

Eklenti için simge

about.txt

Eklenti hakkında bilgi veren bir metin dosyası

translations

Eklentinizin değişik dillere çevirilerini içeren .mo dosyalarını içeren bir dizin. Detaylar için aşağıya bakın.

Şimdi koda bakalım.

__init__.py

Önce, eklenti metadatasını tanımlamak için zorunlu __init__.py:

from calibre.customize import InterfaceActionBase

class InterfacePluginDemo(InterfaceActionBase):
'''
This class is a simple wrapper that provides information about the actual
plugin class. The actual interface plugin class is called InterfacePlugin
and is defined in the ui.py file, as specified in the actual_plugin field
below.

The reason for having two classes is that it allows the command line
calibre utilities to run without needing to load the GUI libraries.
'''
name = 'Interface Plugin Demo'
description = 'An advanced plugin demo'
supported_platforms = ['windows', 'osx', 'linux']
author = 'Kovid Goyal'
version = (1, 0, 0)
minimum_calibre_version = (0, 7, 53)

#: This field defines the GUI plugin class that contains all the code
#: that actually does something. Its format is module_path:class_name
#: The specified class must be defined in the specified module.
actual_plugin = 'calibre_plugins.interface_demo.ui:InterfacePlugin'

def is_customizable(self):
'''
This method must return True to enable customization via
Preferences->Plugins
'''
return True

def config_widget(self):
'''
Implement this method and :meth:`save_settings` in your plugin to
use a custom configuration dialog.

This method, if implemented, must return a QWidget. The widget can have
an optional method validate() that takes no arguments and is called
immediately after the user clicks OK. Changes are applied if and only
if the method returns True.

If for some reason you cannot perform the configuration at this time,
return a tuple of two strings (message, details), these will be
displayed as a warning dialog to the user and the process will be
aborted.

The base class implementation of this method raises NotImplementedError
so by default no user configuration is possible.
'''
# It is important to put this import statement here rather than at the
# top of the module as importing the config class will also cause the
# GUI libraries to be loaded, which we do not want when using calibre
# from the command line
from calibre_plugins.interface_demo.config import ConfigWidget
return ConfigWidget()

def save_settings(self, config_widget):
'''
Save the settings specified by the user with config_widget.

:param config_widget: The widget returned by :meth:`config_widget`.
'''
config_widget.save_settings()

# Apply the changes
ac = self.actual_plugin_
if ac is not None:
ac.apply_settings()

Dikkate değer tek özellik actual_plugin alanıdır. Calibre’de hem komut satırı hem de GUI arayüzleri olduğundan, bunun gibi GUI eklentileri __init__.py dosyasına herhangi bir GUI kütüphanesi yüklememelidir. real_plugin alanı bunu sizin için calibre’ye gerçek eklentinin ZIP arşivinizdeki başka bir dosyada bulunacağını söyleyerek yapar ve bu dosya yalnızca GUI bağlamına yüklenir.

Bunun çalışması için yukarıda tartışıldığı gibi eklenti ZIP dosyanızda bir eklenti-içe aktarma adı-bir_adı.txt dosyanızın olması gerektiğini unutmayın.

Ayrıca eklentinin kullanıcı yapılandırmasının etkinleştirilmesi için bir kaç metod bulunmaktadır. Bunlardan aşağıda söz edilmektedir.

ui.py

Şimdi asıl GUI eklentisini tanımlayan ui.py ye bakalım. Kaynak kod yoğun şekilde yorumlanmıştır, kendini açıklıyor olması lazım:

from calibre.gui2.actions import InterfaceAction
from calibre_plugins.interface_demo.main import DemoDialog

class InterfacePlugin(InterfaceAction):

name = 'Interface Plugin Demo'

# Declare the main action associated with this plugin
# The keyboard shortcut can be None if you dont want to use a keyboard
# shortcut. Remember that currently calibre has no central management for
# keyboard shortcuts, so try to use an unusual/unused shortcut.
action_spec = ('Interface Plugin Demo', None,
'Run the Interface Plugin Demo', 'Ctrl+Shift+F1')

def genesis(self):
# This method is called once per plugin, do initial setup here

# Set the icon for this interface action
# The get_icons function is a builtin function defined for all your
# plugin code. It loads icons from the plugin zip file. It returns
# QIcon objects, if you want the actual data, use the analogous
# get_resources builtin function.
#
# Note that if you are loading more than one icon, for performance, you
# should pass a list of names to get_icons. In this case, get_icons
# will return a dictionary mapping names to QIcons. Names that
# are not found in the zip file will result in null QIcons.
icon = get_icons('images/icon.png', 'Interface Demo Plugin')

# The qaction is automatically created from the action_spec defined
# above
self.qaction.setIcon(icon)
self.qaction.triggered.connect(self.show_dialog)

def show_dialog(self):
# The base plugin object defined in __init__.py
base_plugin_object = self.interface_action_base_plugin
# Show the config dialog
# The config dialog can also be shown from within
# Preferences->Plugins, which is why the do_user_config
# method is defined on the base plugin class
do_user_config = base_plugin_object.do_user_config

# self.gui is the main calibre GUI. It acts as the gateway to access
# all the elements of the calibre user interface, it should also be the
# parent of the dialog
d = DemoDialog(self.gui, self.qaction.icon(), do_user_config)
d.show()

def apply_settings(self):
from calibre_plugins.interface_demo.config import prefs
# In an actual non trivial plugin, you would probably need to
# do something based on the settings in prefs
prefs

main.py

Arayüz Eklenti Tanıtım iletişim penceresini uygulayan asıl mantıksal kısım.

from qt.core import QDialog, QLabel, QMessageBox, QPushButton, QVBoxLayout class DemoDialog(QDialog):

def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config

# The current database shown in the GUI # db is an instance of the class LibraryDatabase from db/legacy.py # This class has many, many methods that allow you to do a lot of # things. For most purposes you should use db.new_api, which has # a much nicer interface from db/cache.py self.db = gui.current_db self.l = QVBoxLayout() self.setLayout(self.l)

self.label = QLabel(prefs['hello_world_msg']) self.l.addWidget(self.label)

self.setWindowTitle('Interface Plugin Demo') self.setWindowIcon(icon)

self.about_button = QPushButton('About', self) self.about_button.clicked.connect(self.about) self.l.addWidget(self.about_button)

self.marked_button = QPushButton( 'Show books with only one format in the calibre GUI', self) self.marked_button.clicked.connect(self.marked) self.l.addWidget(self.marked_button)

self.view_button = QPushButton( 'View the most recently added book', self) self.view_button.clicked.connect(self.view) self.l.addWidget(self.view_button)

self.update_metadata_button = QPushButton( 'Update metadata in a book\'s files', self) self.update_metadata_button.clicked.connect(self.update_metadata) self.l.addWidget(self.update_metadata_button)

self.conf_button = QPushButton( 'Configure this plugin', self) self.conf_button.clicked.connect(self.config) self.l.addWidget(self.conf_button)

self.resize(self.sizeHint())

def about(self): # Get the about text from a file inside the plugin zip file # The get_resources function is a builtin function defined for all your # plugin code. It loads files from the plugin zip file. It returns # the bytes from the specified file. # # Note that if you are loading more than one file, for performance, you # should pass a list of names to get_resources. In this case, # get_resources will return a dictionary mapping names to bytes. Names that # are not found in the zip file will not be in the returned dictionary. text = get_resources('about.txt') QMessageBox.about(self, 'About the Interface Plugin Demo', text.decode('utf-8'))

def marked(self): ''' Show books with only one format ''' db = self.db.new_api matched_ids = {book_id for book_id in db.all_book_ids() if len(db.formats(book_id)) == 1} # Mark the records with the matching ids # new_api does not know anything about marked books, so we use the full # db object self.db.set_marked_ids(matched_ids)

# Tell the GUI to search for all marked records self.gui.search.setEditText('marked:true') self.gui.search.do_search()

def view(self): ''' View the most recently added book ''' most_recent = most_recent_id = None db = self.db.new_api for book_id, timestamp in db.all_field_for('timestamp', db.all_book_ids()).items(): if most_recent is None or timestamp > most_recent: most_recent = timestamp most_recent_id = book_id if most_recent_id is not None: # Get a reference to the View plugin view_plugin = self.gui.iactions['View'] # Ask the view plugin to launch the viewer for row_number view_plugin._view_calibre_books([most_recent_id])

def update_metadata(self): ''' Set the metadata in the files in the selected book's record to match the current metadata in the database. ''' from calibre.ebooks.metadata.meta import set_metadata from calibre.gui2 import error_dialog, info_dialog

# Get currently selected books rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, 'Cannot update metadata', 'No books selected', show=True) # Map the rows to book ids ids = list(map(self.gui.library_view.model().id, rows)) db = self.db.new_api for book_id in ids: # Get the current metadata for this book from the db mi = db.get_metadata(book_id, get_cover=True, cover_as_data=True) fmts = db.formats(book_id) if not fmts: continue for fmt in fmts: fmt = fmt.lower() # Get a python file object for the format. This will be either # an in memory file or a temporary on disk file ffile = db.format(book_id, fmt, as_file=True) ffile.seek(0) # Set metadata in the format set_metadata(ffile, mi, fmt) ffile.seek(0) # Now replace the file in the calibre library with the updated # file. We dont use add_format_with_hooks as the hooks were # already run when the file was first added to calibre. db.add_format(book_id, fmt, ffile, run_hooks=False)

info_dialog(self, 'Updated files', 'Updated the metadata in the files of %d book(s)'%len(ids), show=True)

def config(self): self.do_user_config(parent=self) # Apply the changes self.label.setText(prefs['hello_world_msg'])

Eklenti ZIP dosyasından kaynak alma
calibre’nin eklenti yükleme sistemi, eklenti ZIP dosyasından dosyaları kolayca almanızı sağlayan birkaç yerleşik işlevi tanımlar.

get_resources(isim_veya_isim_listesi)

Bu işlev, ZIP dosyası içindeki dosyaların yollarının listesiyle çağrılmalıdır. Örneğin, ZIP dosyasındaki resimler klasöründeki icon.png dosyasına erişmek için şunu kullanırsınız: images/icon.png. Windows’ta bile her zaman yol ayırıcı olarak eğik çizgi kullanın. Tek bir ad girdiğinizde, işlev o dosyanın ham baytlarını döndürür veya ad ZIP dosyasında bulunamadıysa Yok’u döndürür. Birden fazla ad girerseniz, adları baytlarla eşleştiren bir sözlük döndürür. Bir ad bulunamazsa, döndürülen sözlükte mevcut olmayacaktır.

get_icons(name_or_list_of_names, plugin_name=’’)

get_resources() tarafından döndürülen ham baytlardan QIcon nesneleri oluşturan get_resources() sarmalayıcısı. ZIP dosyasında bir ad bulunamazsa ilgili QIcon boş olacaktır. Simge temasını desteklemek için eklentinizin insan dostu adını eklenti_adı olarak girin. Kullanıcı, eklentiniz için simgeler içeren bir simge teması kullanıyorsa, bunlar tercihen yüklenecektir.

Eklentiniz için kullanıcı yapılandırmasını etkinleştirmek

Kullanıcıların eklentinizi yapılandırmasına izin vermek için, temel eklenti sınıfınızda aşağıda gösterildiği gibi is_customizable, config_widget ve save_settings olmak üzere üç yöntem tanımlamanız gerekir:

def is_customizable(self):
'''
This method must return True to enable customization via
Preferences->Plugins
'''
return True

def config_widget(self):
'''
Implement this method and :meth:`save_settings` in your plugin to
use a custom configuration dialog.

This method, if implemented, must return a QWidget. The widget can have
an optional method validate() that takes no arguments and is called
immediately after the user clicks OK. Changes are applied if and only
if the method returns True.

If for some reason you cannot perform the configuration at this time,
return a tuple of two strings (message, details), these will be
displayed as a warning dialog to the user and the process will be
aborted.

The base class implementation of this method raises NotImplementedError
so by default no user configuration is possible.
'''
# It is important to put this import statement here rather than at the
# top of the module as importing the config class will also cause the
# GUI libraries to be loaded, which we do not want when using calibre
# from the command line
from calibre_plugins.interface_demo.config import ConfigWidget
return ConfigWidget()

def save_settings(self, config_widget):
'''
Save the settings specified by the user with config_widget.

:param config_widget: The widget returned by :meth:`config_widget`.
'''
config_widget.save_settings()

# Apply the changes
ac = self.actual_plugin_
if ac is not None:
ac.apply_settings()

calibre’nin yapılandırma verisi saklamak için bir çok yöntemi vardır (uzun geçmişinden miras). Önerilen metod JSONConfig sınıfının kullanılmasıdır, bu da yapılandırmanızı bir .json dosyasında tutar.

Tanıtım eklentisindeki yapılandırma verisini yöneten kod config.py’de:

from qt.core import QHBoxLayout, QLabel, QLineEdit, QWidget

# This is where all preferences for this plugin will be stored
# Remember that this name (i.e. plugins/interface_demo) is also
# in a global namespace, so make it as unique as possible.
# You should always prefix your config file name with plugins/,
# so as to ensure you dont accidentally clobber a calibre config file
prefs = JSONConfig('plugins/interface_demo')

# Set defaults
prefs.defaults['hello_world_msg'] = 'Hello, World!'

class ConfigWidget(QWidget):

def __init__(self):
QWidget.__init__(self)
self.l = QHBoxLayout()
self.setLayout(self.l)

self.label = QLabel('Hello world &message:')
self.l.addWidget(self.label)

self.msg = QLineEdit(self)
self.msg.setText(prefs['hello_world_msg'])
self.l.addWidget(self.msg)
self.label.setBuddy(self.msg)

def save_settings(self):
prefs['hello_world_msg'] = self.msg.text()

prefs nesnesi şimdi eklenti kodunda basitçe şu şekilde kullanılabilir:

from calibre_plugins.interface_demo.config import prefs

prefs nesnesinin main.py’de kullanıldığını görebilirsiniz:

self.do_user_config(parent=self)
# Apply the changes
self.label.setText(prefs['hello_world_msg'])

Kitap eklentilerini düzenle

Şimdi biraz vites değiştirelim ve calibre kitap düzenleyicisine araçlar eklemek için bir eklenti oluşturmaya bakalım. Eklentiyi burada bulabilirsiniz: editor_demo_plugin.zip [https://calibre-ebook.com/downloads/editor_demo_plugin.zip].

İlk adım, above de açıklandığı gibi tüm eklentiler için boş bir içe aktarma ismi dosyası oluşturmaktır. Eklentiyi plugin-import-name-editor_plugin_demo.txt olarak adlandıralım.

Şimdi eklenti metadatasını içeren – ismi, yazarı, sürümü vs. zorunlu __init__.py dosyasını oluşturuyoruz.

from calibre.customize import EditBookToolPlugin

class DemoPlugin(EditBookToolPlugin):

name = 'Edit Book plugin demo'
version = (1, 0, 0)
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
description = 'A demonstration of the plugin interface for the ebook editor'
minimum_calibre_version = (1, 46, 0)

Tek bir düzenleyici eklenti her biri araç çubuğunda bir simgeye ve düzenleyicide Eklentiler menüsünde bir girdiye denk gelmek üzere bir çok araç sağlayabilir. Aracın birden çok eylemi bulunması durumunda alt menüleri de olabilir.

Araçların her biri eklentinizdeki main.py içinde tanımlanmalıdır. Her araç calibre.gui2.tweak_book.plugin.Tool sınıfından türetilen bir sınıftır. Tanıtım eklentisindeki main.py ye bakalım, kaynak kod yoğun olarak yorumlanmış ve kendini anlatabiliyor durumdadır. Daha fazla detay için calibre.gui2.tweak_book.plugin.Tool sınıfının API belgelerini okuyun.

main.py

Burda kitaptaki tüm yazı tipi boyutlarını kullanıcı tarafından sağlanan bir numarayla çarpan tek bir araç tanımı göreceğiz. Bu araç kendi eklentilerinizi geliştirirken ihtiyaç duyacağınız çeşitli önemli kavramı gösterir, yani (yoğun bir şekilde yorumlanmış) kaynak kodu dikkatli okumalısınız.

import re from calibre import force_unicode from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES, serialize from calibre.gui2 import error_dialog

# The base class that all tools must inherit from from calibre.gui2.tweak_book.plugin import Tool from css_parser.css import CSSRule from qt.core import QAction, QInputDialog class DemoTool(Tool):

#: Set this to a unique name it will be used as a key name = 'demo-tool'

#: If True the user can choose to place this tool in the plugins toolbar allowed_in_toolbar = True

#: If True the user can choose to place this tool in the plugins menu allowed_in_menu = True def create_action(self, for_toolbar=True): # Create an action, this will be added to the plugins toolbar and # the plugins menu ac = QAction(get_icons('images/icon.png'), 'Magnify fonts', self.gui) # noqa if not for_toolbar: # Register a keyboard shortcut for this toolbar action. We only # register it for the action created for the menu, not the toolbar, # to avoid a double trigger self.register_shortcut(ac, 'magnify-fonts-tool', default_keys=('Ctrl+Shift+Alt+D',)) ac.triggered.connect(self.ask_user) return ac def ask_user(self): # Ask the user for a factor by which to multiply all font sizes factor, ok = QInputDialog.getDouble( self.gui, 'Enter a magnification factor', 'Allow font sizes in the book will be multiplied by the specified factor', value=2, min=0.1, max=4 ) if ok: # Ensure any in progress editing the user is doing is present in the container self.boss.commit_all_editors_to_container() try: self.magnify_fonts(factor) except Exception: # Something bad happened report the error to the user import traceback error_dialog(self.gui, _('Failed to magnify fonts'), _( 'Failed to magnify fonts, click "Show details" for more info'), det_msg=traceback.format_exc(), show=True) # Revert to the saved restore point self.boss.revert_requested(self.boss.global_undo.previous_container) else: # Show the user what changes we have made, allowing her to # revert them if necessary self.boss.show_current_diff() # Update the editor UI to take into account all the changes we # have made self.boss.apply_container_update_to_gui()

def magnify_fonts(self, factor): # Magnify all font sizes defined in the book by the specified factor # First we create a restore point so that the user can undo all changes # we make. self.boss.add_savepoint('Before: Magnify fonts')

container = self.current_container # The book being edited as a container object

# Iterate over all style declarations in the book, this means css # stylesheets, <style> tags and style="" attributes for name, media_type in container.mime_map.items(): if media_type in OEB_STYLES: # A stylesheet. Parsed stylesheets are css_parser CSSStylesheet # objects. self.magnify_stylesheet(container.parsed(name), factor) container.dirty(name) # Tell the container that we have changed the stylesheet elif media_type in OEB_DOCS: # A HTML file. Parsed HTML files are lxml elements for style_tag in container.parsed(name).xpath('//*[local-name="style"]'): if style_tag.text and style_tag.get('type', None) in {None, 'text/css'}: # We have an inline CSS <style> tag, parse it into a # stylesheet object sheet = container.parse_css(style_tag.text) self.magnify_stylesheet(sheet, factor) style_tag.text = serialize(sheet, 'text/css', pretty_print=True) container.dirty(name) # Tell the container that we have changed the stylesheet for elem in container.parsed(name).xpath('//*[@style]'): # Process inline style attributes block = container.parse_css(elem.get('style'), is_declaration=True) self.magnify_declaration(block, factor) elem.set('style', force_unicode(block.getCssText(separator=' '), 'utf-8'))

def magnify_stylesheet(self, sheet, factor): # Magnify all fonts in the specified stylesheet by the specified # factor. for rule in sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE): self.magnify_declaration(rule.style, factor)

def magnify_declaration(self, style, factor): # Magnify all fonts in the specified style declaration by the specified # factor val = style.getPropertyValue('font-size') if not val: return # see if the font-size contains a number num = re.search(r'[0-9.]+', val) if num is not None: num = num.group() val = val.replace(num, '%f' % (float(num) * factor)) style.setProperty('font-size', val) # We should also be dealing with the font shorthand property and # font sizes specified as non numbers, but those are left as exercises # for the reader
main.py yi kısımlara ayıralım. Magnify fonts adında tek bir araç tanımladığını görüyoruz. Bu araç kullanıcıya bir sayı soracak ve kitaptaki tüm yazı tipi boyutlarını bu sayıyla çarpacak.

İlk önemli nokta, bu araç için anahtar olarak kullanılacağından benzersiz bir karakter dizisi olarak ayarlayacağınız araç ismidir.

Sonraki önemli nokta da calibre.gui2.tweak_book.plugin.Tool.create_action(). Bu metod eklenti araç çubuğu ve eklenti menüsünde görünen QAction nesnelerini oluşturur. Bunun yanında, isteğe bağlı olarak kullanıcının özelleştirebileceği bir klavye kısayolu atar. QAction tarafından tetiklenen sinyal kullanıcıya yazı tipi boyutu çarpanını soran ask_user() metoduna bağlıdır, bu da çarpma kodunu çalıştırır.

Çarpma kodu iyi belgelenmiş ve oldukça basittir. Dikkate alınacan temel şeyler düzenleyici pencereye self.gui ile, düzenleyici Boss una self.boss ile bir referans aldığınızdır. Boss düzenleyici kullanıcı arayüzünü kontrol eden nesnedir. calibre.gui2.tweak_book.boss.Boss sınıfında belgelenmiş bir çok faydalı metodu vardır.

Son olarak calibre.ebooks.oeb.polish.container.Container nesnesi olan düzenlenen kitaba referans olan self.current_container bulunmaktadır. Bu, kitabı, kitabı oluşturan HTML/CSS/resim dosyaları koleksiyonu olarak temsil eder ve daha bir çok faydalı iş yapan metodu vardır. Eklenti kodunuzda tekrar kullanabileceğiniz bir çok kullanışlı fonksiyon ve kapsayıcı nesne E-kitap düzenleme araçları için API belgeleri içinde belgelenmiştir.

Eklentinize çeviriler eklemek

Eklentinizdeki tüm kullanıcı arayüzü karakter dizilerini ana calibre kullanıcı arayüzü için hangi dil seçildiyse o dile çevirip gösterebilirsiniz.

İlk adım eklentinizin kaynak kodunun üstünden geçmek ve kullanıcıya görünür tüm metinleri _() içerisine alarak çevrilebilir olarak işaretlemektir. Örneğin:

action_spec = (_('My plugin'), None, _('My plugin is cool'), None)

Daha sonra eklenti kaynak kodunuzdan .po dosyaları oluşturmak için bir program kullanın. Çevirmek istediğiniz her dil için bir .po dosyası bulunmalıdır. Örneğin: Almanca için de.po, Fransızca için fr.po vb. Bunun için Poedit [https://poedit.net/] programını kullanabilirsiniz.

Bu .po dosyalarını çevirmenlerinize gönderin. Bunları geri aldıktan sonra .mo dosyaları halinde derleyin. Bunun için tekrar Poedit’i kullanabilir veya şunu yapabilirsiniz:

calibre-debug -c "from calibre.translations.msgfmt import main; main()" filename.po

.mo dosyalarını eklentinizdeki translations dizinine koyun.

Son adım eklentinizin .py dosyalarının başına load_translations() fonksiyonunu eklemektir. Performans açısından bu fonksiyonu yalnızca çevrilebilir karakter dizilerinin olduğu dosyalara eklemelisiniz. Yani tipik bir kullanıcı arayüzü eklentisinde ui.py başında çağırırken ``__init__.py``de çağırmazsınız.

Eklentilerinizin çevirilerini calibre’de Tercihler → Arayüz → Görünüm ve his altında kullanıcı arayüzü dilini değiştirerek veya calibre’yi CALIBRE_OVERRIDE_LANG ortam değişken seti ile çalıştırarak test edebilirsiniz. Örneğin:

CALIBRE_OVERRIDE_LANG=de

de yi test etmek istediğiniz dil koduyla değiştirin.

Çoğul içeren çeviriler için _() yerine ngettext() işlevini kullanın. Örneğin:

ngettext('Delete a book', 'Delete {} books', num_books).format(num_books)

Eklenti API’si

Yukarda fark etmiş olabileceğiniz gibi, calibre’de eklenti bir sınıftır. Calibre’de değişik türde eklentiler için değişik sınıflar bulunur. Tüm eklentiler için temel sınıf dahil her sınıfla ilgili detay Eklentiler için API belgeleri içinde bulunabilir.

Eklentiniz çoğu durumda calibre’den de kod kullanacaktır. calibre kod tabanındaki fonksiyonellikle ilgili çeşitli bilgiler bulmak için, calibre’deki Kod düzeni kısmını okuyun.

Eklentilerde hata ayıklama

İlk, en önemli adım calibre’yi hata ayıklama kipinde çalıştırmaktır. Bu işlemi komut satırından şu şekilde yapabilirsiniz:

calibre-debug -g

Veya calibre içinden Tercihler düğmesine sağ tıklayarak veya Ctrl+Shift+R klavye kısayolunu kullanarak.

Komut satırından çalıştırırken, hata ayıklama çıktısı konsola yazdırılacaktır, calibre içinden çalıştırılırken çıktı bir txt dosyasına gider.

Yazdırma ifadelerini eklenti kodunuzun herhangi bir yerine ekleyebilirsiniz, bunlar hata ayıklama modunda yayınlanacaktır. Unutmayın, bu Python, hata ayıklamak için print ifadelerinden daha fazlasına ihtiyacınız olmamalı ;) Calibre’nin tamamını sadece bu hata ayıklama tekniğini kullanarak geliştirdim.

Eklentinize yaptığınızı değişiklikleri hızlıca şu komut satırıyla test edebilirsiniz:

calibre-debug -s; calibre-customize -b /path/to/your/plugin/folder; calibre

Bu işlem calibre’yi kapatır, kapanmanın bitmesini bekler, eklentinizi calibre’de günceller ve calibre’yi baştan başlatır.

Daha fazla eklenti örneği

Birçok gelişmiş Calibre eklentisinin listesini burada <https://www.mobileread.com/forums/showthread.php?t=118764>`_ bulabilirsiniz.

Eklentilerinizi başkalarıyla paylaşmak

Oluşturduğunuz eklentileri diğer calibre kullanıcılarıyla paylaşmak istiyorsanız, eklentinizi ‘calibre eklentileri forumunda <https://www.mobileread.com/forums/forumdisplay.php?f=237 yeni bir başlıkta yayınlayın. >`_.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »
- E-kitaplarda matematik dizgisi

E-kitaplarda matematik dizgisi

Calibre E-kitap görüntüleyicisi e-kitaplara (EPUB ve HTML dosyaları) gömülü matematiği görüntüleme yeteneğine sahiptir. Matematiği doğrudan TeX veya MathML veya AsciiMath ile dizebilirsiniz. Calibre E-kitap görüntüleyici bunu yapmak için mükemmel MathJax [https://www.mathjax.org] kütüphanesini kullanır. Bu, calibre E-kitap görüntüleyiciyle iyi çalışan, içinde matematik bulunan e-kitaplar oluşturmaya ilişkin kısa bir eğitimdir.

Matematik ifadeleri ile basit bir HTML dosyası

Basit bir HTML dosyasının içine satır içi matematik yazabilirsiniz ve calibre E-kitap görüntüleyici bunu düzgün dizilmiş matematik haline getirecektir. Aşağıdaki örnekte matematik için TeX gösterimini kullanıyoruz. Ve işaretlerinin ve küçüktür ve büyüktür işaretlerinin sırasıyla & < ve > olarak yazılması gerektiğine dair küçük bir uyarı ile normal TeX komutlarını kullanabileceğinizi göreceksiniz.

İlk adım calibre’ye bunun matematik içereceğini söylemektir. Bunu, aşağıdaki kod <head>parçasını HTML dosyasının bölümüne ekleyerek yapabilirsiniz:

<script type="text/x-mathjax-config"></script>

İşte bu kadar, artık bir .tex dosyasına yazdığınız gibi matematiksel ifadeleri yazabilirsiniz. Örneğin Lorenz denklemlerini böyle yazabilirsiniz:

<h2>The Lorenz Equations</h2>

<p>
\begin{align}
\dot{x} &amp; = \sigma(y-x) \\
\dot{y} &amp; = \rho x - y - xz \\
\dot{z} &amp; = -\beta z + xy
\end{align}
</p>

Bu parça calibre E-kitap görüntüleyicide aşağıdaki ekran görüntüsüne benziyor.

Lorentz Denklemleri

Daha fazla denklem ve satır içi matematik içeren eksiksiz HTML dosyası aşağıda yeniden üretilmiştir. Başkalarına kolayca dağıtabileceğiniz bir e-kitap elde etmek için bu HTML dosyasını calibre’de EPUB’a dönüştürebilirsiniz.

<!DOCTYPE html> <html> <!-- Copyright (c) 2012 Design Science, Inc. --> <head> <title>Math Test Page</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8" />

<!-- This script tag is needed to make calibre's ebook-viewer recpgnize that this file needs math typesetting --> <script type="text/x-mathjax-config"> // This line adds numbers to all equations automatically, unless explicitly suppressed. MathJax.tex = {tags: 'all'}; </script>

<style> h1 {text-align:center} h2 { font-weight: bold; background-color: #DDDDDD; padding: .2em .5em; margin-top: 1.5em; border-top: 3px solid #666666; border-bottom: 2px solid #999999; } </style> </head> <body>

<h1>Sample Equations</h1>

<h2>The Lorenz Equations</h2>

<p> \begin{align} \dot{x} &amp; = \sigma(y-x) \label{lorenz}\\ \dot{y} &amp; = \rho x - y - xz \\ \dot{z} &amp; = -\beta z + xy \end{align} </p>

<h2>The Cauchy-Schwarz Inequality</h2>

<p>\[ \left( \sum_{k=1}^n a_k b_k \right)^{\!\!2} \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) \]</p>

<h2>A Cross Product Formula</h2>

<p>\[ \mathbf{V}_1 \times \mathbf{V}_2 = \begin{vmatrix} \mathbf{i} &amp; \mathbf{j} &amp; \mathbf{k} \\ \frac{\partial X}{\partial u} &amp; \frac{\partial Y}{\partial u} &amp; 0 \\ \frac{\partial X}{\partial v} &amp; \frac{\partial Y}{\partial v} &amp; 0 \\ \end{vmatrix} \]</p>

<h2>The probability of getting \(k\) heads when flipping \(n\) coins is:</h2>

<p>\[P(E) = {n \choose k} p^k (1-p)^{ n-k} \]</p>

<h2>An Identity of Ramanujan</h2>

<p>\[ \frac{1}{(\sqrt{\phi \sqrt{5}}-\phi) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } } \]</p>

<h2>A Rogers-Ramanujan Identity</h2>

<p>\[ 1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}, \quad\quad \text{for $|q|&lt;1$}. \]</p>

<h2>Maxwell's Equations</h2>

<p> \begin{align} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &amp; = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} &amp; = 4 \pi \rho \\ \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} &amp; = \vec{\mathbf{0}} \\ \nabla \cdot \vec{\mathbf{B}} &amp; = 0 \end{align} </p>

<h2>In-line Mathematics</h2>

<p>While display equations look good for a page of samples, the ability to mix math and text in a paragraph is also important. This expression \(\sqrt{3x-1}+(1+x)^2\) is an example of an inline equation. As you see, equations can be used this way as well, without unduly disturbing the spacing between lines.</p>

<h2>References to equations</h2>

<p>Here is a reference to the Lorenz Equations (\ref{lorenz}). Clicking on the equation number will take you back to the equation.</p>

</body> </html>

Daha fazla bilgi

Calibre E-kitap görüntüleyici matematiği işlemek için MathJax kütüphanesini kullandığından, e-kitaplarda matematik hakkında daha fazla bilgi edinmek ve yardım almak için en iyi yer MathJax web sitesi [https://www.mathjax.org]’dur.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »
- AZW3 • EPUB • MOBI katalogları oluşturma

AZW3 • EPUB • MOBI katalogları oluşturma
calibre’nin katalog oluşturma özelliği kitaplığınızda çeşitli biçimlerde dosyalardan oluşan bir katalog oluşturmanıza imkan verir. Bu yardım dosyası AZW3, EPUB ve MOBI biçimlerinde bir katalog oluşturmanın genel seçeneklerini tanımlar.

- Katalog için kitap seçmek

- İçerilen bölümler

- Önekler

- Hariç tutulan kitaplar

- Hariç tutulan türler

- Diğer seçenekler

- Özel katalog kapakları

- Ek yardım kaynakları

Katalog için kitap seçmek

Kitaplığınızdaki tüm kitapların kataloglanmasını isterseniz ana penceredeki tüm arama ve filtreleme seçeneklerini kaldırın. Seçilmiş tek bir dosyayla kitaplığınızdaki tüm dosyalar oluşturulan kataloğa dahil edilmeye aday olur. Çeşitli kriterlerle bazı kitaplar katalog dışında tutulabilir; daha fazla bilgi için aşağıdaki Hariç tutulan türler alanına bakın.

Kitaplığınızdaki bazı kitapların kataloglanmasını isterseniz iki seçeneğiniz var:

- Kataloglamak istediğiniz kitapların bir çoklu seçimini oluşturun. Calibre’nin ana penceresinde birden çok kitap seçildiğinde sadece seçilen kitaplar kataloglanır.

- Görüntülenen kitapları filtrelemek için Arama alanını veya Etiket tarayıcıyı kullanın. Sadece görüntülenen kitaplar kataloglanacaktır.

Katalog oluşturmaya başlamak için, Kitapları dönüştür > Calibre kitaplığınızdaki kitapların bir kataloğunu oluşturun menü öğesini seçin. Katalog oluştur iletişim kutusuna daha kolay erişim için Tercihler > Arayüz > Araç Çubukları ve menüler içindeki bir araç çubuğuna Katalog oluştur düğmesini de ekleyebilirsiniz.

Katalog seçeneklerinde Katalog biçimi olarak AZW3, EPUB veya MOBI seçin. Katalog başlığı alanına oluşturulacak katalog için bir isim girin. Girdiğiniz isimde bir katalog zaten mevcutsa oluşturulacak katalog onun üzerine yazılacaktır.

Kataloğu aygıta otomatik olarak gönder seçeneğini etkinleştirmek oluşturulan kataloğun bağlanan aygıta hemen indirilmesine neden olacaktır.

İçerilen bölümler

Onay işaretiyle etkinleştirilmiş bölümler oluşturulacak kataloğa dahil edilecektir:

- Yazarlar - tüm kitaplar yazarlarına göre sıralı halde liste biçiminde sunulur. Serisi olmayan kitaplar serilerden önce gösterilir.

- Başlıklar - tüm kitaplar başlıklarına göre sıralı halde liste biçiminde sunulur.

- Seriler - bir serinin parçası olan tüm kitaplar serilerine göre sıralı halde liste biçiminde sunulur.

- Türler - türler bir liste halinde Yazar ve Serilerine göre sıralı olarak sunulurlar.

- Yakın Zamanda Eklenen - tüm kitaplar, ters tarih sırasıyla. Liste son 30 günde eklenen kitapları içerir, sonra eklenen kitapların aydan aya listesini.

- Tanımlamalar - her kitap için kapağın küçük resmi ve yorumlar dahil ayrıntılı tanımlamalar bulunur. Yazara göre sıralıdır ve serisi olmayan kitaplar daha önce gösterilir.

Önekler

Ön ek kuralları belirli bir kritere uyan kitaplara ön ek eklemenize imkan verir. Örneğin okuduğunuz kitapları bir onay işaretiyle veya istek listenizdeki kitapları bir X ile işaretlemek isteyebilirsiniz.

İlk sütundaki onay kutusu kuralı etkinleştirir. İsim sizin girdiğiniz kuralın ismidir. Alan ise ya Etiketler veya kitaplığınızdaki özel bir sütun olabilir. Değer de Alan alanının eşleştiği içeriktir. Bir ön ek kuralı sağlandığında kitap seçilen Ön Ek ile işaretlenecektir.

Yukarıdaki örnekte üç ön ek kuralı belirtilmiştir:

1. Okunmuş kitap, Son okuma özel sütununda bulunan herhangi tarihteki bir kitabın önüne bir kontrol işareti geleceğini belirtir.

2. Dileklistesi öğesi Dileklistesi etiketi olan herhangi kitabın önüne bir X sembolü geleceğini belirtir.

3. Kitaplık, Kitaplıkta Mevcut özel sütununda Doğru (veya Evet) değeri olan her kitabın önüne bir çift ok sembolü geleceğini belirtir.

Ilk eşleşen kuralı öneki sağlar. Engelli veya eksik kurallar göz ardı edilir.

Hariç tutulan kitaplar

Hariç tutma kuralları kataloglanmayacak kitapları belirtmenize imkan verir.

İlk sütundaki onay kutusu kuralı etkinleştirir. İsim kural için girdiğiniz isimdir. Alan ise ya Etiket ya da kitaplığınızdaki özel bir sütundur. Değer alanı Alan ile eşleşen içeriktir. Dışarıda tutma kurallarından biri sağlandığında kitap oluşturulan kataloğun dışında tutulur.

Yukarıdaki örnekte iki hariç tutma kuralı belirtilmiştir:

1. Kataloglar kuralı Katalog etiketine sahip kitapların oluşturulacak kataloğa dahil edilmeyeceğini belirtir.

2. Arşivlenmiş Kitaplar kuralı Durum sütunundaki değeri Arşivlenmiş olan kitapların oluşturulacak kataloğa dahil edilmeyeceğini belirtir.

Tüm kurallar bütün kitaplar için çalıştırılır. Kapalı veya eksik kurallar yok sayılır.

Hariç tutulan türler

Bir katalog oluşturulduğunda veritabanınızdaki etiketler türler olarak kullanılır. Örneğin; etiketleri Kurgu ve Kurgusal Olmayan olarak kullanabilirsiniz. Bu etiketler oluşturulan katalogda türler haline gelir ve bu etiketlerle etiketlenmiş kitaplar ilgili türlerin altında listelenir. Bir kitap taşıdığı bütün etiketlere karşılık gelen türlerin altında listelenir.

Belirli etiketleri belirli amaçlar için kullanabilirsiniz. Örneğin; bir + işareti kitabı okumayı veya [Amazon Freebie] gibi parantezli bir ifade kitabın kaynağını işaret edebilir. Hariç tutulan türler düzenli ifadesi oluşturulan kataloğa alınmasını istemediğiniz etiketleri belirtmenize izin verir. Varsayılan hariç tutma düzenli ifade deseni \[.+\]\+ [etiket] şeklinde yazılan etiketleri hariç tutar. Kitap okuma için varsayılan etiket olan + kataloğa alınmayacak türleri belirtir.

Bir düzenli ifadede ayrıca tam isim kullanabilirsiniz. Örneğin, [Amazon Freebie] veya [Project Gutenberg]. Çıkarılması için birden çok tam etiket listelemek istiyorsanız, aralarına bir boru (dikey çubuk) koyun: [Amazon Freebie]|[Project Gutenberg].

Düzenli ifade sonucu veritabanınızdaki etiketlere ve girdiğiniz düzenli ifade desenine göre katalog oluşturulduğunda hangi etiketlerin hariç tutulacağını gösterir. Düzenli ifade desenini düzenlediğinizde sonuçlar da güncellenir.

Diğer seçenekler

Katalog kapağı yeni bir kapak mı oluşturulacağını yoksa mevcut bir kapağın mı kullanılacağını belirtir. Kataloglarınız için özel bir kapak oluşturmak mümkündür - daha fazla bilgi için bkz. Özel katalog kapakları. Yeniden kullanmak istediğiniz özel bir kapak oluşturduysanız :guilabel:`Mevcut kapağı kullan`ı seçin. Aksi halde :guilabel:`Yeni kapak oluştur`u seçin.

Ekstra Açıklama notu, Açıklama sayfasına, kapak küçük resminin yanına eklenecek özel bir sütun içeriğini belirtir. Örneğin, Son Okuma özel sütununu kullanarak bir kitabı en son okuduğunuz tarihi görüntülemek isteyebilirsiniz. Açıklama notu özelliğinin gelişmiş kullanımı için, calibre forumundaki bu gönderiye <https://www.mobileread.com/forums/showpost.php?p=1335767&postcount=395>`_ bakın.

Thumb genişliği, Açıklama sayfalarında bulunan kapak küçük resimleri için genişlik tercihini belirtir. Performansı artırmak için küçük resimler önbelleğe alınır. Farklı genişliklerle denemeler yapmak için, tercih ettiğiniz genişliği belirleyene kadar yalnızca birkaç kitaptan oluşan bir katalog oluşturmayı deneyin, ardından kataloğunuzun tamamını oluşturun. Yeni bir küçük resim genişliğiyle bir katalog ilk kez oluşturulduğunda performans daha yavaş olacaktır, ancak kataloğun sonraki derlemeleri küçük resim önbelleğinden yararlanacaktır.

Yorumlarla birleştir, katalog oluşturma sırasında içeriği bozulmadan yorum meta verileriyle birleştirilecek özel bir sütunu belirtir. Örneğin, yorumların meta verilerine eklemek istediğiniz özel bir Yazar biyografisi sütununuz olabilir. Özel sütun içeriklerini yorumlar bölümünün öncesine veya sonrasına eklemeyi seçebilir ve isteğe bağlı olarak eklenen içeriği yatay bir kural ayırıcıyla ayırabilirsiniz. Uygun özel sütun türleri arasında “metin, yorumlar ve bileşik” yer alır.

Özel katalog kapakları

Generate Cover eklentisi [https://www.mobileread.com/forums/showthread.php?t=124219] yüklendiğinde, kataloğunuz için özel kapaklar oluşturabilirsiniz. Eklentiyi yüklemek için Tercihler > Gelişmiş > Eklentiler > Yeni eklentileri al seçeneğine gidin.

Ek yardım kaynakları

Calibre’nin Katalog özelliği hakkında daha fazla bilgi için, nasıl yapılacağı hakkında bilgi bulabileceğiniz MobileRead forumu yapışkan Katalog Oluşturma - Buradan başlayın [https://www.mobileread.com/forums/showthread.php?t=118556] konusuna bakın. katalog şablonlarını özelleştirmek ve bir hata raporunun nasıl gönderileceğini öğrenmek için.

Soru sormak veya calibre’nin Katalog özelliğini diğer kullanıcılarla tartışmak için MobileRead forumunu Kitaplık Yönetimi [https://www.mobileread.com/forums/forumdisplay.php?f=236] ziyaret edin.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Rehberler »
- Sanal kitaplıklar

Sanal kitaplıklar

Calibre’de Sanal kitaplık, calibre’ye normal bir kitaplığın yalnızca bir alt kümesini açmasını söylemenin bir yoludur. Örneğin, yalnızca belirli bir yazarın kitaplarıyla veya yalnızca belirli bir etikete sahip kitaplarla çalışmak isteyebilirsiniz. Büyük kitap koleksiyonunuzu daha küçük alt koleksiyonlara bölmenin tercih edilen yolu Sanal kitaplıkları kullanmaktır. Kitaplığınızı birden çok küçük kitaplığa bölmekten daha üstündür, çünkü koleksiyonunuzun tamamında arama yapmak istediğinizde tam kitaplığa geri dönebilirsiniz. Calibre’de aynı anda birden fazla ayrı kitaplıkta arama yapmanın bir yolu yoktur.

Sanal kütüphane basit bir aramadan farklıdır. Bir arama yalnızca kitap listesinde gösterilen kitapların listesini kısıtlayacaktır. Sanal kütüphane bunu yapar ve ayrıca soldaki :guilabel:`Tag tarayıcı`da gösterilen girişleri de kısıtlar. Etiket tarayıcısı yalnızca Sanal kütüphanedeki kitaplardan gelen etiketleri, yazarları, dizileri, yayıncıları vb. gösterir. Bu nedenle bir Sanal kütüphane, gerçek kütüphane yalnızca kısıtlı kitap setini içeriyormuş gibi davranır.

Sanal kitaplıklar oluşturma

Sanal kitaplığı kullanmak için Arama çubuğunun solunda bulunan Sanal kitaplık düğmesine tıklayın ve Sanal kitaplık oluştur seçeneğini seçin. İlk örnek olarak, bize yalnızca belirli bir yazarın kitaplarını gösteren bir Sanal kütüphane oluşturalım. Aşağıdaki resimde gösterildiği gibi Yazarlar linkine tıklayın ve kullanmak istediğiniz yazarı seçin ve Tamam’a tıklayın.

Sanal kitaplık oluştur iletişim kutusu sizin için dolduruldu. Tamam’ı tıkladığınızda, yalnızca seçilen yazarın kitaplarını görüntüleyen yeni bir Sanal kütüphanenin oluşturulduğunu ve otomatik olarak bu kütüphaneye geçildiğini göreceksiniz. Calibre söz konusu olduğunda kütüphaneniz sanki sadece seçilen yazarın kitaplarını içeriyormuş gibi.

Sanal kitaplık`a bir kez daha tıklayarak ve :guilabel:`<None> adlı girişi seçerek istediğiniz zaman tam kitaplığa geri dönebilirsiniz.

Sanal kütüphaneler aramalara dayalıdır. Herhangi bir aramayı Sanal kitaplığın temeli olarak kullanabilirsiniz. Sanal kütüphane yalnızca o aramayla eşleşen kitapları içerecektir. Öncelikle, kullanmak istediğiniz aramayı Arama çubuğuna yazın veya Tag tarayıcısını kullanarak bir arama oluşturun. Döndürülen sonuçlardan memnun olduğunuzda, Sanal kitaplık düğmesine tıklayın, Kütüphane oluştur`u seçin ve yeni Sanal kitaplık için bir ad girin. Daha sonra Sanal kitaplık, az önce yazdığınız aramaya dayalı olarak oluşturulacaktır. Aramalar çok güçlüdür, onlarla yapabileceğiniz türde şeylerin örnekleri için, bkz. :ref:`search_interface.

Yararlı Sanal kitaplık örnekleri

- Son gün içinde calibre’ye eklenen kitaplar:

date:>1daysago

- Son ay içinde calibre’ye eklenen kitaplar:

date:>30daysago

- 5 yıldız almış kitaplar:

rating:5

- En az 4 yıldız almış kitaplar:

rating:>=4

- Değerlendirmesi yapılmamış kitaplar:

rating:false

- Calibre içinden Haberleri Getir işlevi ile indirilmiş dergiler:

tags:=News and author:=calibre

- Etiketi olmayan kitaplar:

tags:false

- Kapağı olmayan kitaplar:

cover:false

Sanal kitaplıklarla çalışma

:guilabel:`Sanal kitaplık`a tıklayıp uygun eylemi seçerek önceden oluşturulmuş bir Sanal kitaplığı düzenleyebilir veya kaldırabilirsiniz.

Geçerli kitaplık açıldığında calibre’ye her zaman belirli bir Sanal kitaplığı uygulamak istediğinizi :guilabel:`Tercihler->Arayüz->Davranış`a giderek söyleyebilirsiniz.

Sanal kitaplık düğmesine tıklayıp *geçerli arama girişini seçerek mevcut aramayı geçici bir Sanal kitaplık olarak hızlı bir şekilde kullanabilirsiniz.

Mevcut tüm Sanal kitaplıkları kitap listesinin üzerinde sekmeler halinde görüntüleyebilirsiniz. Sanal kitaplıklar arasında çok sık geçiş yapmayı seviyorsanız bu özellikle kullanışlıdır. Sanal kitaplık düğmesine tıklayın ve :guilabel:`Sanal kitaplıkları sekmeler olarak göster`i seçin. Sekmeleri sürükleyip bırakarak yeniden düzenleyebilir ve görmek istemediklerinizi kapatabilirsiniz. Kapatılan sekmeler, sekme çubuğuna sağ tıklanarak geri yüklenebilir.

Aramalarda Sanal kitaplıkları kullanma

Sanal kitaplıktaki kitapları vl: önekini kullanarak arayabilirsiniz. Örneğin, vl:Read Read Sanal kütüphanesindeki tüm kitapları bulacaktır. vl:Read ve vl:"Bilim Kurgu" araması, hem Read hem de Bilim Kurgu Sanal kitaplıklarında bulunan tüm kitapları bulacaktır.

vl: sonrasındaki değer bir Sanal kitaplığın adı olmalıdır. Sanal kitaplık adı boşluk içeriyorsa, onu tırnak işaretleri içine alın.

Sanal kitaplık aramasının bir kullanımı İçerik sunucusundadır. Tercihler → Ağ üzerinden paylaşım → Kullanıcı adı ve parola iste bölümünde kullanıcının görebileceği calibre kitaplıklarını sınırlayabilirsiniz. Görünen her kitaplık için, hangi kitapların görüleceğini daha da sınırlamak amacıyla bir arama ifadesi belirleyebilirsiniz. Kitapları Sanal kitaplıktakilerle sınırlamak için vl:"Sanal kitaplık adı" kullanın.

Ek kısıtlamalar kullanmak

Ek kısıtlamalar`ı kullanarak Sanal kütüphanede gösterilen kitapları daha da kısıtlayabilirsiniz. Sanal kitaplıkta gösterilen kitapları daha da kısıtlamak için geçerli Sanal kitaplığa uygulanabilecek, önceden oluşturduğunuz kayıtlı arama kısıtlamasıdır. Örneğin, :guilabel:`Tarihsel Kurgu olarak etiketlenen kitaplar için bir Sanal kitaplığınız ve okunmamış kitapları gösteren kayıtlı bir aramanız olduğunu varsayalım, Sanal Kitaplık düğmesini tıklayıp Ek seçeneğini seçebilirsiniz. yalnızca okunmamış Tarihi Kurgu kitaplarını göstermek için kısıtlama seçeneği. Kaydedilen aramalar hakkında bilgi edinmek için bkz. :ref:`saved_searches.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- The calibre:// URL scheme

The calibre:// URL scheme
calibre registers itself as the handler program for calibre:// URLs. So you can use these to perform actions like opening books, searching for books, etc from other programs/documents or via the command line. For example, running the following at the command line:

calibre calibre://switch-library/Some_Library

Will open calibre with the library named Some Library. Library names are the folder name of the library folder with spaces replaced by underscores. The special value _ means the current library. The various types of URLs are documented below.

Bu bağlantıları HTML dosyalarının veya Word belgelerinin veya benzerlerinin içine bile yerleştirebilirsiniz ve işletim sistemi belirtilen eylemi gerçekleştirmek için calibre’yi otomatik olarak çalıştıracaktır.

- Belirli bir kitaplığa geçiş

- Calibre’de belirli bir kitabı göster

- E-kitap görüntüleyicide belirli bir konumda belirli bir kitabı açın

- Kitap aranıyor

- Open a book details window on a book in some library

- Open the notes associated with an author/series/etc.

- URL parametrelerinin hex kodlaması

Belirli bir kitaplığa geçiş

URL sözdizimi şöyledir:

calibre://switch-library/Library_Name

Library names are the folder name of the library with spaces replaced by underscores. The special value _ means the current library. You can also use hex encoding for the library names, useful if the library names have special characters that would otherwise require URL encoding. Hex encoded library names look like:

_hex_-AD23F4BC

Where the part after the _hex_- prefix is the library name encoded as UTF-8 and every byte represented by two hexadecimal characters.

Calibre’de belirli bir kitabı göster

URL sözdizimi şöyledir:

calibre://show-book/Library_Name/book_id

This will show the book with book_id (a number) in calibre. The ids for books can be seen in the calibre interface by hovering over the Click to open link in the Book details panel, it is the number in brackets at the end of the path to the book folder.

You can copy a link to the current book displayed in calibre by right clicking the Book details panel and choosing Copy link to book.

Bir arama etkinse ve kitap aramayla eşleşmezse, arama silinir.

If a Virtual library is selected, calibre will use it when showing the book. If the book isn’t found in that virtual library then the virtual library is cleared.

Kitabı gösterirken belirli bir Sanal kitaplığa geçmek istiyorsanız şunu kullanın:

calibre://show-book/Library_Name/book_id?virtual_library=Library%20Name
or
calibre://show-book/Library_Name/book_id?encoded_virtual_library=hex_encoded_virtual_library_name

replacing spaces in the Virtual library name by %20. If the book isn’t found in that virtual library then the virtual library is ignored.

E-kitap görüntüleyicide belirli bir konumda belirli bir kitabı açın

URL sözdizimi şöyledir:

calibre://view-book/Library_Name/book_id/book_format?open_at=location

Here, book_format is the format of the book, for example, EPUB or MOBI and the location is an optional location inside the book. The easiest way to get these links is to open a book in the viewer, then in the viewer controls select Go to → Location and there such a link will be given that you can copy/paste elsewhere.

Kitap aranıyor

URL sözdizimi şöyledir:

calibre://search/Library_Name?q=query
calibre://search/Library_Name?eq=hex_encoded_query

Here query is any valid search expression. If the search expression is complicated, encode it as a hex string and use eq instead. Leaving out the query will cause the current search to be cleared.

By default, if a Virtual library is selected, calibre will clear it before doing the search to ensure all books are found. If you want to preserve the Virtual library, use:

calibre://search/Library_Name?q=query&virtual_library=_

Belirli bir Sanal kitaplığa geçmek istiyorsanız şunu kullanın:

calibre://search/Library_Name?virtual_library=Library%20Name
or
calibre://search/Library_Name?encoded_virtual_library=hex_encoded_virtual_library_name

replacing spaces in the Virtual library name by %20.

If you perform a search in calibre and want to generate a link for it you can do so by right clicking the search bar and choosing Copy search as URL.

Open a book details window on a book in some library

URL sözdizimi şöyledir:

calibre://book-details/Library_Name/book_id

This opens a book details window on the specified book from the specified library without changing the current library or the selected book.

Open the notes associated with an author/series/etc.

URL sözdizimi şöyledir:

calibre://book-details/Library_Name/Field_Name/id_Item_Id

This opens a window showing the notes of the specified item. The easiest way to create such URLs is to show the notes you want in calibre and click the Copy URL button to copy the URL to the clipboard and paste it wherever you need.

Here Field_Name is the name of the columns such as authors or tags. For user created columns, replace the leading # in the field name with an underscore, so #mytags becomes _mytags.

In addition to specifying items by id using Item_Id you can also specify them by name using either val_Item_Name or hex_Hex_Encoded_Item_Name. For example:

calibre://book-details/Library_Name/authors/val_John%20Doe

URL parametrelerinin hex kodlaması

Hex encoding of URL parameters is done by first encoding the parameter as UTF-8 bytes, and then replacing each byte by two hexadecimal characters representing the byte. For example, the string abc is the bytes 0x61 0x62 and 0x63 in UTF-8, so the encoded version is the string: 616263.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Calibre’yi özelleştirmek

Calibre’yi özelleştirmek
calibre oldukça modüler bir tasarıma sahiptir. Çeşitli parçaları özelleştirilebilir. Burada şunları öğreneceksiniz:

- calibre’nin davranışını özelleştirmek için ortam değişkenlerinin ve ince ayarların nasıl kullanılacağı,

- varsayılanları geçersiz kılmak için simgeler ve şablonlar gibi kendi statik kaynaklarınızı nasıl belirleyeceğiniz

- calibre’ye işlevsellik eklemek için eklentiler nasıl kullanılır?

- simge temaları ve eklentilerinin diğer calibre kullanıcılarıyla nasıl paylaşılacağı.

- calibre’ye yeni çevrimiçi içerik kaynakları eklemek amacıyla tariflerin nasıl oluşturulacağını görmek için haberler Bölümünü ziyaret edin.

Not

Simge temaları ve eklentileri calibre’nin yerleşik güncelleyicisi aracılığıyla indekslenmiş ve indirilebilir olmasına rağmen, bunların calibre’nin parçası olmadığını ve destek ve kaynak kodu için kanonik konumlarının Mobileread forumlarında [https://www.mobileread.com/forumsolduğunuunutmayın./forumdisplay.php?f=166] destek başlıklarında.

- Çevresel değişkenler

- İnce Ayarlar

- Simgelerin, şablonların ve benzerlerinin üzerine yazmak

- Calibre için özel ikon teman oluşturuluyor

- Eklentilerle calibre’yi özelleştirmek

Çevresel değişkenler

- CALIBRE_CONFIG_DIRECTORY - yapılandırma dosyalarının saklandığı/okunduğu klasörü ayarlar.

- CALIBRE_TEMP_DIR - calibre tarafından kullanılan geçici klasörü ayarlar

- CALIBRE_CACHE_DIRECTORY - calibre’nin oturumlar arasında kalıcı verileri önbelleğe almak için kullandığı klasörü ayarlar

- CALIBRE_OVERRIDE_DATABASE_PATH - metadata.db için tam yolu belirtmenize izin verir. Bu değişkeni kullanarak metadata.db dosyasını kitaplık dizininin dışında tutabilirsiniz. Bu kitaplık dizininizi ağ üzerinde dosya kilitleme özelliği olmayan bir diskte tutuyorsanız faydalı olacaktır.

- CALIBRE_DEVELOP_FROM - calibre geliştirme ortamından çalıştırmak için kullanılır. Bakınız geliştirme.

- CALIBRE_OVERRIDE_LANG - arayüz tarafından kullanılan dili zorlamak için kullanılır (ISO 639 dil kodu)

- CALIBRE_TEST_TRANSLATION - bir çeviri .po dosyasını test etmek için kullanılır (.po dosyasının yolu olmalıdır)

- CALIBRE_NO_NATIVE_FILEDIALOGS - calibre’nin dosyaları/klasörleri seçmek için yerel dosya iletişim kutularını kullanmamasına neden olur.

- CALIBRE_NO_NATIVE_MENUBAR - calibre’nin Ubuntu Unity ve benzeri Linux masaüstü ortamlarında yerel (global) bir menü oluşturmamasına neden olur. Menü, geleneksel olduğu gibi pencerenin içine yerleştirilir.

- CALIBRE_USE_SYSTEM_THEME - varsayılan olarak Linux’ta calibre kendi yerleşik Qt stilini kullanır. Bunun amacı Qt calibre’nin oluşturulduğu sürüm ile Qt sistemi arasındaki uyumsuzluklardan kaynaklanan çökmeleri ve takılmaları önlemektir. Dezavantajı ise calibre’nin sistemin görünüşünü ve hissini takip edememesidir. Eğer bu ortam değişkenini Linux’ta ayarlarsanız, calibre’nin sistem temasını kullanmasına neden olur – çökmelere ve takılmalara karşı dikkatli olun.

- CALIBRE_SHOW_DEPRECATION_WARNINGS - calibre’nin kullanımdan kaldırma uyarılarını stdout’a yazdırmasına neden olur. Calibre geliştiricileri için kullanışlıdır.

- CALIBRE_NO_DEFAULT_PROGRAMS - calibre’nin Windows ile işleyebildiği dosya türlerini otomatik olarak kaydetmesini önleyin.

- QT_QPA_PLATFORM - Linux’ta calibre’yi Wayland kullanmaya zorlamak için bunu ``wayland``a ve X11 kullanımını zorlamak için ``xcb``ye ayarlayın.

- SYSFS_PATH - sysfs, /sys dizininden başka bir dizine bağlıysa kullanın

- http_proxy, https_proxy - Linux’ta bir HTTP(S) proxy belirtmek için kullanılır

Windows’ta ortam değişkenleri nasıl ayarlanır [https://www.computerhope.com/issues/ch000549.htm] konusuna bakın. MacOS kullanıyorsanız, ~/Library/Preferences/calibre/macos-env.txt dosyasını oluşturarak ve ortam değişkenlerini her satıra bir tane koyarak ortam değişkenlerini ayarlayabilirsiniz, örneğin:

CALIBRE_DEVELOP_FROM=$HOME/calibre-src/src
CALIBRE_NO_NATIVE_FILEDIALOGS=1
CALIBRE_CONFIG_DIRECTORY=~/.config/calibre

İnce Ayarlar

İnce ayarlar calibre davranışının çeşitli yönlerini kontrol etmek için belirtebileceğiniz küçük değişikliklerdir. Bunları Tercihler -> Gelişmiş -> İnce Ayarlar alanından değiştirebilirsiniz. İnce ayarlar için varsayılan değerler aşağıdadır

#!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai # License: GPLv3 Copyright: 2010, Kovid Goyal <kovid at kovidgoyal.net>

# Contains various tweaks that affect calibre behavior. Only edit this file if # you know what you are doing. If you delete this file, it will be recreated from # defaults.

#: Auto increment series index # The algorithm used to assign a book added to an existing series a series number. # New series numbers assigned using this tweak are always integer values, except # if a constant non-integer is specified. # Possible values are: # next - First available integer larger than the largest existing number # first_free - First available integer larger than 0 # next_free - First available integer larger than the smallest existing number # last_free - First available integer smaller than the largest existing number. Return largest existing + 1 if no free number is found # const - Assign the number 1 always # no_change - Do not change the series index # a number - Assign that number always. The number is not in quotes. Note that 0.0 can be used here. # Examples: # series_index_auto_increment = 'next' # series_index_auto_increment = 'next_free' # series_index_auto_increment = 16.5 # # Set the use_series_auto_increment_tweak_when_importing tweak to True to # use the above values when importing/adding books. If this tweak is set to # False (the default) then the series number will be set to 1 if it is not # explicitly set during the import. If set to True, then the # series index will be set according to the series_index_auto_increment setting. # Note that the use_series_auto_increment_tweak_when_importing tweak is used # only when a value is not provided during import. If the importing regular # expression produces a value for series_index, or if you are reading metadata # from books and the import plugin produces a value, then that value will # be used irrespective of the setting of the tweak. series_index_auto_increment = 'next' use_series_auto_increment_tweak_when_importing = False

#: Add separator after completing an author name # Set this if the completion separator should be appended to the end of the # completed text to automatically begin a new completion operation for authors. # It can be either True or False authors_completer_append_separator = False

#: Author sort name algorithm # The algorithm used to copy author to author_sort. # Possible values are: # invert: use "fn ln" -> "ln, fn" # copy : copy author to author_sort without modification # comma : use 'copy' if there is a ',' in the name, otherwise use 'invert' # nocomma : "fn ln" -> "ln fn" (without the comma) # When this tweak is changed, the author_sort values stored with each author # must be recomputed by right-clicking on an author in the left-hand tags # panel, selecting 'Manage authors', and pressing # 'Recalculate all author sort values'. # # The author_name_suffixes are words that are ignored when they occur at the # end of an author name. The case of the suffix is ignored and trailing # periods are automatically handled. # # The same is true for author_name_prefixes. # # The author_name_copywords are a set of words which, if they occur in an # author name, cause the automatically generated author sort string to be # identical to the author's name. This means that the sort for a string like # "Acme Inc." will be "Acme Inc." instead of "Inc., Acme". # # If author_use_surname_prefixes is enabled, any of the words in # author_surname_prefixes will be treated as a prefix to the surname, if they # occur before the surname. So for example, "John von Neumann" would be sorted # as "von Neumann, John" and not "Neumann, John von". author_sort_copy_method = 'comma' author_name_suffixes = ('Jr', 'Sr', 'Inc', 'Ph.D', 'Phd', 'MD', 'M.D', 'I', 'II', 'III', 'IV', 'Junior', 'Senior') author_name_prefixes = ('Mr', 'Mrs', 'Ms', 'Dr', 'Prof') author_name_copywords = ( 'Agency', 'Corporation', 'Company', 'Co.', 'Council', 'Committee', 'Inc.', 'Institute', 'National', 'Society', 'Club', 'Team', 'Software', 'Games', 'Entertainment', 'Media', 'Studios', ) author_use_surname_prefixes = False author_surname_prefixes = ('da', 'de', 'di', 'la', 'le', 'van', 'von')

#: Splitting multiple author names # By default, calibre splits a string containing multiple author names on # ampersands and the words "and" and "with". You can customize the splitting # by changing the regular expression below. Strings are split on whatever the # specified regular expression matches, in addition to ampersands. # Default: r'(?i),?\s+(and|with)\s+' authors_split_regex = r'(?i),?\s+(and|with)\s+'

#: Use author sort in Tag browser # Set which author field to display in the Tag browser (the list of authors, # series, publishers etc on the left hand side). The choices are author and # author_sort. This tweak affects only what is displayed under the authors # category in the Tag browser and Content server. Please note that if you set this # to author_sort, it is very possible to see duplicate names in the list because # although it is guaranteed that author names are unique, there is no such # guarantee for author_sort values. Showing duplicates won't break anything, but # it could lead to some confusion. When using 'author_sort', the tooltip will # show the author's name. # Examples: # categories_use_field_for_author_name = 'author' # categories_use_field_for_author_name = 'author_sort' categories_use_field_for_author_name = 'author'

#: Control partitioning of Tag browser # When partitioning the Tag browser, the format of the subcategory label is # controlled by a template: categories_collapsed_name_template if sorting by # name, categories_collapsed_rating_template if sorting by average rating, and # categories_collapsed_popularity_template if sorting by popularity. There are # two variables available to the template: first and last. The variable 'first' # is the initial item in the subcategory, and the variable 'last' is the final # item in the subcategory. Both variables are 'objects'; they each have multiple # values that are obtained by using a suffix. For example, first.name for an # author category will be the name of the author. The sub-values available are: # name: the printable name of the item # count: the number of books that references this item # avg_rating: the average rating of all the books referencing this item # sort: the sort value. For authors, this is the author_sort for that author # category: the category (e.g., authors, series) that the item is in. # Note that the "r'" in front of the { is necessary if there are backslashes # (\ characters) in the template. It doesn't hurt anything to leave it there # even if there aren't any backslashes. categories_collapsed_name_template = r'{first.sort:shorten(4,,0)} - {last.sort:shorten(4,,0)}' categories_collapsed_rating_template = r'{first.avg_rating:4.2f:ifempty(0)} - {last.avg_rating:4.2f:ifempty(0)}' categories_collapsed_popularity_template = r'{first.count:d} - {last.count:d}'

#: Specify columns to sort the booklist by on startup # Provide a set of columns to be sorted on when calibre starts. # The argument is None if saved sort history is to be used # otherwise it is a list of column,order pairs. Column is the # lookup/search name, found using the tooltip for the column # Order is 0 for ascending, 1 for descending. # For example, set it to [('authors',0),('title',0)] to sort by # title within authors. sort_columns_at_startup = None

#: Control how dates are displayed # Format to be used for publication date and the timestamp (date). # A string controlling how the publication date is displayed in the GUI # d the day as number without a leading zero (1 to 31) # dd the day as number with a leading zero (01 to 31) # ddd the abbreviated localized day name (e.g. 'Mon' to 'Sun'). # dddd the long localized day name (e.g. 'Monday' to 'Sunday'). # M the month as number without a leading zero (1-12) # MM the month as number with a leading zero (01-12) # MMM the abbreviated localized month name (e.g. 'Jan' to 'Dec'). # MMMM the long localized month name (e.g. 'January' to 'December'). # yy the year as two digit number (00-99) # yyyy the year as four digit number # h the hours without a leading 0 (0 to 11 or 0 to 23, depending on am/pm) ' # hh the hours with a leading 0 (00 to 11 or 00 to 23, depending on am/pm) ' # m the minutes without a leading 0 (0 to 59) ' # mm the minutes with a leading 0 (00 to 59) ' # s the seconds without a leading 0 (0 to 59) ' # ss the seconds with a leading 0 (00 to 59) ' # ap use a 12-hour clock instead of a 24-hour clock, with "ap" replaced by the localized string for am or pm # AP use a 12-hour clock instead of a 24-hour clock, with "AP" replaced by the localized string for AM or PM # iso the date with time and timezone. Must be the only format present # For example, given the date of 9 Jan 2010, the following formats show # MMM yyyy ==> Jan 2010 yyyy ==> 2010 dd MMM yyyy ==> 09 Jan 2010 # MM/yyyy ==> 01/2010 d/M/yy ==> 9/1/10 yy ==> 10 # # publication default if not set: MMM yyyy # timestamp default if not set: dd MMM yyyy # last_modified_display_format if not set: dd MMM yyyy gui_pubdate_display_format = 'MMM yyyy' gui_timestamp_display_format = 'dd MMM yyyy' gui_last_modified_display_format = 'dd MMM yyyy'

#: Control sorting of titles and series in the library display # Control title and series sorting in the library view. If set to # 'library_order', the title sort field will be used instead of the title. # Unless you have manually edited the title sort field, leading articles such as # The and A will be ignored. If set to 'strictly_alphabetic', the titles will be # sorted as-is (sort by title instead of title sort). For example, with # library_order, The Client will sort under 'C'. With strictly_alphabetic, the # book will sort under 'T'. # This flag affects calibre's library display. It has no effect on devices. In # addition, titles for books added before changing the flag will retain their # order until the title is edited. Editing a title and hitting Enter # without changing anything is sufficient to change the sort. Or you can use # the 'Update title sort' action in the Bulk metadata edit dialog to update # it for many books at once. title_series_sorting = 'library_order'

#: Control formatting of title and series when used in templates # Control how title and series names are formatted when saving to disk/sending # to device. The behavior depends on the field being processed. If processing # title, then if this tweak is set to 'library_order', the title will be # replaced with title_sort. If it is set to 'strictly_alphabetic', then the # title will not be changed. If processing series, then if set to # 'library_order', articles such as 'The' and 'An' will be moved to the end. If # set to 'strictly_alphabetic', the series will be sent without change. # For example, if the tweak is set to library_order, "The Lord of the Rings" # will become "Lord of the Rings, The". If the tweak is set to # strictly_alphabetic, it would remain "The Lord of the Rings". Note that the # formatter function raw_field will return the base value for title and # series regardless of the setting of this tweak. save_template_title_series_sorting = 'library_order'

#: Set the list of words considered to be "articles" for sort strings # Set the list of words that are to be considered 'articles' when computing the # title sort strings. The articles differ by language. By default, calibre uses # a combination of articles from English and whatever language the calibre user # interface is set to. In addition, in some contexts where the book language is # available, the language of the book is used. You can change the list of # articles for a given language or add a new language by editing # per_language_title_sort_articles. To tell calibre to use a language other # than the user interface language, set, default_language_for_title_sort. For # example, to use German, set it to 'deu'. A value of None means the user # interface language is used. The setting title_sort_articles is ignored # (present only for legacy reasons). per_language_title_sort_articles = { # English 'eng' : (r'A\s+', r'The\s+', r'An\s+'), # Esperanto 'epo': (r'La\s+', r"L'", 'L´'), # Spanish 'spa' : (r'El\s+', r'La\s+', r'Lo\s+', r'Los\s+', r'Las\s+', r'Un\s+', r'Una\s+', r'Unos\s+', r'Unas\s+'), # French 'fra' : (r'Le\s+', r'La\s+', r"L'", u'L´', u'L’', r'Les\s+', r'Un\s+', r'Une\s+', r'Des\s+', r'De\s+La\s+', r'De\s+', r"D'", r'D´', r'D’'), # Polish 'pol': (), # Italian 'ita': ('Lo\\s+', 'Il\\s+', "L'", 'L´', 'La\\s+', 'Gli\\s+', 'I\\s+', 'Le\\s+', 'Uno\\s+', 'Un\\s+', 'Una\\s+', "Un'", 'Un´', 'Dei\\s+', 'Degli\\s+', 'Delle\\s+', 'Del\\s+', 'Della\\s+', 'Dello\\s+', "Dell'", 'Dell´'), # Portuguese 'por' : (r'A\s+', r'O\s+', r'Os\s+', r'As\s+', r'Um\s+', r'Uns\s+', r'Uma\s+', r'Umas\s+', ), # Romanian 'ron' : (r'Un\s+', r'O\s+', r'Nişte\s+', ), # German 'deu' : (r'Der\s+', r'Die\s+', r'Das\s+', r'Den\s+', r'Ein\s+', r'Eine\s+', r'Einen\s+', r'Dem\s+', r'Des\s+', r'Einem\s+', r'Eines\s+'), # Dutch 'nld' : (r'De\s+', r'Het\s+', r'Een\s+', r"'n\s+", r"'s\s+", r'Ene\s+', r'Ener\s+', r'Enes\s+', r'Den\s+', r'Der\s+', r'Des\s+', r"'t\s+"), # Swedish 'swe' : (r'En\s+', r'Ett\s+', r'Det\s+', r'Den\s+', r'De\s+', ), # Turkish 'tur' : (r'Bir\s+', ), # Afrikaans 'afr' : (r"'n\s+", r'Die\s+', ), # Greek 'ell' : (r'O\s+', r'I\s+', r'To\s+', r'Ta\s+', r'Tus\s+', r'Tis\s+', r"'Enas\s+", r"'Mia\s+", r"'Ena\s+", r"'Enan\s+", ), # Hungarian 'hun' : (r'A\s+', r'Az\s+', r'Egy\s+',), } default_language_for_title_sort = None title_sort_articles=r'^(A|The|An)\s+'

#: Specify a folder calibre should connect to at startup # Specify a folder that calibre should connect to at startup using # connect_to_folder. This must be a full path to the folder. If the folder does # not exist when calibre starts, it is ignored. # Example for Windows: # auto_connect_to_folder = 'C:/Users/someone/Desktop/testlib' # Example for other operating systems: # auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library' auto_connect_to_folder = ''

#: Specify renaming rules for SONY collections # Specify renaming rules for SONY collections. This tweak is only applicable if # metadata management is set to automatic. Collections on SONYs are named # depending upon whether the field is standard or custom. A collection derived # from a standard field is named for the value in that field. # # For example, if the standard 'series' column contains the value 'Darkover', then the # collection name is 'Darkover'. A collection derived from a custom field will # have the name of the field added to the value. For example, if a custom series # column named 'My Series' contains the name 'Darkover', then the collection # will by default be named 'Darkover (My Series)'. For purposes of this # documentation, 'Darkover' is called the value and 'My Series' is called the # category. If two books have fields that generate the same collection name, # then both books will be in that collection. # # This set of tweaks lets you specify for a standard or custom field how # the collections are to be named. You can use it to add a description to a # standard field, for example 'Foo (Tag)' instead of the 'Foo'. You can also use # it to force multiple fields to end up in the same collection. # # For example, you could force the values in 'series', '#my_series_1', and # '#my_series_2' to appear in collections named 'some_value (Series)', thereby # merging all of the fields into one set of collections. # # There are two related tweaks. The first determines the category name to use # for a metadata field. The second is a template, used to determines how the # value and category are combined to create the collection name. # The syntax of the first tweak, sony_collection_renaming_rules, is: # {'field_lookup_name':'category_name_to_use', 'lookup_name':'name', ...} # # The second tweak, sony_collection_name_template, is a template. It uses the # same template language as plugboards and save templates. This tweak controls # how the value and category are combined together to make the collection name. # The only two fields available are {category} and {value}. The {value} field is # never empty. The {category} field can be empty. The default is to put the # value first, then the category enclosed in parentheses, it isn't empty: # '{value} {category:|(|)}' # # Examples: The first three examples assume that the second tweak # has not been changed. # # 1) I want three series columns to be merged into one set of collections. The # column lookup names are 'series', '#series_1' and '#series_2'. I want nothing # in the parenthesis. The value to use in the tweak value would be: # sony_collection_renaming_rules={'series':'', '#series_1':'', '#series_2':''} # # 2) I want the word '(Series)' to appear on collections made from series, and # the word '(Tag)' to appear on collections made from tags. Use: # sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'} # # 3) I want 'series' and '#myseries' to be merged, and for the collection name # to have '(Series)' appended. The renaming rule is: # sony_collection_renaming_rules={'series':'Series', '#myseries':'Series'} # # 4) Same as example 2, but instead of having the category name in parentheses # and appended to the value, I want it prepended and separated by a colon, such # as in Series: Darkover. I must change the template used to format the category name # # The resulting two tweaks are: # sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'} # sony_collection_name_template='{category:||: }{value}' sony_collection_renaming_rules={} sony_collection_name_template='{value}{category:| (|)}'

#: Specify how SONY collections are sorted # Specify how SONY collections are sorted. This tweak is only applicable if # metadata management is set to automatic. You can indicate which metadata is to # be used to sort on a collection-by-collection basis. The format of the tweak # is a list of metadata fields from which collections are made, followed by the # name of the metadata field containing the sort value. # Example: The following indicates that collections built from pubdate and tags # are to be sorted by the value in the custom column '#mydate', that collections # built from 'series' are to be sorted by 'series_index', and that all other # collections are to be sorted by title. If a collection metadata field is not # named, then if it is a series- based collection it is sorted by series order, # otherwise it is sorted by title order. # [(['pubdate', 'tags'],'#mydate'), (['series'],'series_index'), (['*'], 'title')] # Note that the bracketing and parentheses are required. The syntax is # [ ( [list of fields], sort field ) , ( [ list of fields ] , sort field ) ] # Default: empty (no rules), so no collection attributes are named. sony_collection_sorting_rules = []

#: Control how tags are applied when copying books to another library # Set this to True to ensure that tags in 'Tags to add when adding # a book' are added when copying books to another library add_new_book_tags_when_importing_books = False

#: Set the maximum number of sort 'levels' # Set the maximum number of sort 'levels' that calibre will use to resort the # library after certain operations such as searches or device insertion. Each # sort level adds a performance penalty. If the database is large (thousands of # books) the penalty might be noticeable. If you are not concerned about multi- # level sorts, and if you are seeing a slowdown, reduce the value of this tweak. maximum_resort_levels = 5

#: Choose whether dates are sorted using visible fields # Date values contain both a date and a time. When sorted, all the fields are # used, regardless of what is displayed. Set this tweak to True to use only # the fields that are being displayed. sort_dates_using_visible_fields = False

#: Fuzz value for trimming covers # The value used for the fuzz distance when trimming a cover. # Colors within this distance are considered equal. # The distance is in absolute intensity units. cover_trim_fuzz_value = 10

#: Control behavior of the book list # You can control the behavior of double clicks and pressing Enter on the books # list. Choices: open_viewer, do_nothing, show_book_details, # show_locked_book_details, edit_cell, edit_metadata. Selecting anything other # than open_viewer, show_book_details, or show_locked_book_details has the side # effect of disabling editing a field using a single click. # Default: open_viewer. # Example: doubleclick_on_library_view = 'do_nothing' # You can also control whether the book list scrolls per item or # per pixel. Default is per item. doubleclick_on_library_view = 'open_viewer' enter_key_behavior = 'do_nothing' horizontal_scrolling_per_column = False vertical_scrolling_per_row = False

#: Language to use when sorting # Setting this tweak will force sorting to use the # collating order for the specified language. This might be useful if you run # calibre in English but want sorting to work in the language where you live. # Set the tweak to the desired ISO 639-1 language code, in lower case. # You can find the list of supported locales at # https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes # Default: locale_for_sorting = '' -- use the language calibre displays in # Example: locale_for_sorting = 'fr' -- sort using French rules. # Example: locale_for_sorting = 'nb' -- sort using Norwegian rules. locale_for_sorting = ''

#: The number of seconds to wait before sending emails # The number of seconds to wait before sending emails when using a # public email server like GMX/Hotmail/Gmail. Default is: 5 minutes # Setting it to lower may cause the server's SPAM controls to kick in, # making email sending fail. Changes will take effect only after a restart of # calibre. You can also change the list of hosts that calibre considers # to be public relays here. Any relay host ending with one of the suffixes # in the list below will be considered a public email server. public_smtp_relay_delay = 301 public_smtp_relay_host_suffixes = ['gmail.com', 'live.com', 'gmx.com', 'outlook.com']

#: The maximum width and height for covers saved in the calibre library # All covers in the calibre library will be resized, preserving aspect ratio, # to fit within this size. This is to prevent slowdowns caused by extremely # large covers maximum_cover_size = (1650, 2200)

#: Where to send downloaded news # When automatically sending downloaded news to a connected device, calibre # will by default send it to the main memory. By changing this tweak, you can # control where it is sent. Valid values are "main", "carda", "cardb". Note # that if there isn't enough free space available on the location you choose, # the files will be sent to the location with the most free space. send_news_to_device_location = "main"

#: Unified toolbar on macOS # If you enable this option and restart calibre, the toolbar will be 'unified' # with the titlebar as is normal for macOS applications. However, doing this has # various bugs, for instance the minimum width of the toolbar becomes twice # what it should be and it causes other random bugs on some systems, so turn it # on at your own risk! unified_title_toolbar_on_osx = False

#: Save original file when converting/polishing from same format to same format # When calibre does a conversion from the same format to the same format, for # example, from EPUB to EPUB, the original file is saved, so that in case the # conversion is poor, you can tweak the settings and run it again. By setting # this to False you can prevent calibre from saving the original file. # Similarly, by setting save_original_format_when_polishing to False you can # prevent calibre from saving the original file when polishing. save_original_format = True save_original_format_when_polishing = True

#: Number of recently viewed books to show # Right-clicking the "View" button shows a list of recently viewed books. Control # how many should be shown, here. gui_view_history_size = 15

#: Change the font size of the Book details panel in the interface # Change the font size at which book details are rendered in the side panel and # comments are rendered in the metadata edit dialog. Set it to a positive or # negative number to increase or decrease the font size. change_book_details_font_size_by = 0

#: What format to default to when using the "Unpack book" feature # The "Unpack book" feature of calibre allows direct editing of a book format. # If multiple formats are available, calibre will offer you a choice # of formats, defaulting to your preferred output format if it is available. # Set this tweak to a specific value of 'EPUB' or 'AZW3' to always default # to that format rather than your output format preference. # Set to a value of 'remember' to use whichever format you chose last time you # used the "Unpack book" feature. # Examples: # default_tweak_format = None (Use output format) # default_tweak_format = 'EPUB' # default_tweak_format = 'remember' default_tweak_format = None

#: Do not preselect a completion when editing authors/tags/series/etc. # This means that you can make changes and press Enter and your changes will # not be overwritten by a matching completion. However, if you wish to use the # completions you will now have to press Tab to select one before pressing # Enter. Which technique you prefer will depend on the state of metadata in # your library and your personal editing style. # # If preselect_first_completion is False and you want Tab to accept what you # typed instead of the first completion then set tab_accepts_uncompleted_text # to True. If you do this then to select from the completions you must press # the Down or Up arrow keys. The tweak tab_accepts_uncompleted_text is ignored # if preselect_first_completion is True preselect_first_completion = False tab_accepts_uncompleted_text = False

#: Completion mode when editing authors/tags/series/etc. # By default, when completing items, calibre will show you all the candidates # that start with the text you have already typed. You can instead have it show # all candidates that contain the text you have already typed. To do this, set # completion_mode to 'contains'. For example, if you type asi it will match both # Asimov and Quasimodo, whereas the default behavior would match only Asimov. completion_mode = 'prefix'

#: Sort the list of libraries alphabetically # The list of libraries in the Copy to library and Quick switch menus are # normally sorted by most used. However, if there are more than a certain # number of such libraries, the sorting becomes alphabetic. You can set that # number here. The default is ten libraries. many_libraries = 10

#: Choose available output formats for conversion # Restrict the list of available output formats in the conversion dialogs. # For example, if you only want to convert to EPUB and AZW3, change this to # restrict_output_formats = ['EPUB', 'AZW3']. The default value of None causes # all available output formats to be present. restrict_output_formats = None

#: Set the thumbnail image quality used by the Content server # The quality of a thumbnail is largely controlled by the compression quality # used when creating it. Set this to a larger number to improve the quality. # Note that the thumbnails get much larger with larger compression quality # numbers. # The value can be between 50 and 99 content_server_thumbnail_compression_quality = 75

#: Image file types to treat as e-books when dropping onto the "Book details" panel # Normally, if you drop any image file in a format known to calibre onto the # "Book details" panel, it will be used to set the cover. If you want to store # some image types as e-books instead, you can set this tweak. # Examples: # cover_drop_exclude = {'tiff', 'webp'} cover_drop_exclude = ()

#: Exclude fields when copy/pasting metadata # You can ask calibre to not paste some metadata fields when using the # Edit metadata->Copy metadata/Paste metadata actions. For example, # exclude_fields_on_paste = ['cover', 'timestamp', '#mycolumn'] # to prevent pasting of the cover, Date and custom column, mycolumn. # You can also add a shortcut in Preferences->Shortcuts->Edit metadata # to paste metadata ignoring this tweak. exclude_fields_on_paste = []

#: Skip internet connected check # Skip checking whether the internet is available before downloading news. # Useful if for some reason your operating systems network checking # facilities are not reliable (for example NetworkManager on Linux). skip_network_check = False

#: Tab stop width in the template editor # Sets the width of the tab stop in the template editor in "average characters". # For example, a value of 1 results in a space with the width of one average character. template_editor_tab_stop_width = 4

#: Value for undefined numbers when sorting # Sets the value to use for undefined numbers when sorting. # For example, the value -10 sorts undefined numbers as if they were set to -10. # Use 'maximum' for the largest possible number. Use 'minimum' for the smallest # possible number. Quotes are optional if entering a number. # Examples: # value_for_undefined_numbers_when_sorting = -100 # value_for_undefined_numbers_when_sorting = '2' # value_for_undefined_numbers_when_sorting = -0.01 # value_for_undefined_numbers_when_sorting = 'minimum' # value_for_undefined_numbers_when_sorting = 'maximum' value_for_undefined_numbers_when_sorting = 0

#: Allow template database functions in composite columns # If True then the template database functions book_values() and book_count() # can be used in composite custom columns. Note: setting this tweak to True and # using these functions in composites can be very slow. # Default: False allow_template_database_functions_in_composites = False

#: Change the programs that are run when opening files/URLs # By default, calibre passes URLs to the operating system to open using # whatever default programs are configured there. Here you can override # that by specifying the program to use, per URL type. For local files, # the type is "file" and for web links it is "http*". For example: # openers_by_scheme = { "http*": "firefox %u" } will make calibre run Firefox # for https://whatever URLs. %u is replaced by the URL to be opened. The scheme # takes a glob pattern allowing a single entry to match multiple URL types. openers_by_scheme = {}

#: Set the first day of the week for calendar popups # It must be one of the values Default, Sunday, Monday, Tuesday, Wednesday, # Thursday, Friday, or Saturday, all in English, spelled exactly as shown. calendar_start_day_of_week = 'Default'

Simgelerin, şablonların ve benzerlerinin üzerine yazmak

Not
calibre’nin simge temaları için doğrudan desteği vardır, calibre için çeşitli simge temaları mevcuttur ve bunları Tercihler → Arayüz → Görüntü ve Hisset → Simge temasını değiştir seçeneğine giderek kullanabilirsiniz. Bireysel simgeleri geçersiz kılmak yerine simge temalarının kullanılması tercih edilir.

calibre simgeler, JavaScript ve meta veri ceketi, kataloglar vb. için şablonlar gibi statik kaynakları beğendiğiniz özelleştirilmiş sürümlerle geçersiz kılmanıza olanak tanır. Tüm statik kaynaklar calibre kurulum konumunun kaynaklar alt klasöründe saklanır. Windows’ta bu genellikle C:\Program Files\Calibre2\app\resources olur. MacOS’ta, /Applications/calibre.app/Contents/Resources/resources/. Linux’ta, calibre web sitesinden ikili yükleyiciyi kullanıyorsanız /opt/calibre/resources olacaktır. Bu yollar calibre’yi nereye kurmayı seçtiğinize bağlı olarak değişebilir.

Calibre’yi bir sonraki güncellemenizde değişikliklerin üzerine yazılacağı için bu kaynaklar klasöründeki dosyaları değiştirmemelisiniz. Bunun yerine Tercihler → Gelişmiş → Çeşitli seçeneğine gidin ve Calibre yapılandırma klasörünü aç`a tıklayın. Bu yapılandırma klasöründe kaynaklar adında bir alt klasör oluşturun ve geçersiz kılmak istediğiniz dosyaları bu klasöre yerleştirin. Dosyaları uygun alt klasörlere yerleştirin, örneğin görüntüleri :file:`resources/images içine yerleştirin, vb. calibre bir sonraki başlatılışında yerleşik dosya yerine otomatik olarak özel dosyanızı kullanacaktır.

Örneğin, Kitapları kaldır eyleminin simgesini değiştirmek istiyorsanız, önce yerleşik kaynaklar klasörüne bakarsınız ve ilgili dosyanın resources/images/remove_books.png olduğunu görürsünüz. '. PNG formatında :file:`my_remove_books.png adında alternatif bir simgeniz olduğunu varsayarsak, bunu yapılandırma klasörüne resources/images/remove_books.png olarak kaydedersiniz. Calibre kullanıcı arayüzü tarafından kullanılan tüm simgeler resources/images ve onun alt klasörlerindedir. Buraya bir geçersiz kılma dosyası yerleştirmek, özel simge temasından bile daha yüksek önceliğe sahip olacaktır.

Calibre için özel ikon teman oluşturuluyor

Güzel bir simge seti oluşturduysanız ve bunları calibre’nin yerleşik simge teması desteği aracılığıyla diğer calibre kullanıcılarıyla paylaşmak istiyorsanız, simgelerinizi kolayca bir tema halinde paketleyebilirsiniz. Bunu yapmak için :guilabel:`Tercihler->Çeşitli->Simge teması oluştur`a gidin, simgelerinizi koyduğunuz klasörü seçin. Ardından tema meta verilerini doldurun ve Tamam'a tıklayın. Bu, tema simgelerini içeren bir ZIP dosyasıyla sonuçlanacaktır. Bunu `Mobileread <https://www.mobileread.com/forums/forumdisplay.php?f=166>`__ adresindeki calibre forumuna yükleyebilirsiniz ve ardından temanızı calibre’nin yerleşik simge tema sistemi aracılığıyla kullanılabilir hale getireceğim. Varsayılan olarak az önce oluşturduğunuz simge teması aynı zamanda calibre’de geçerli tema olarak yüklenecektir. Temanızı test ediyorsanız, temadaki simgelerin kullanılabilmesi için resources/images klasöründeki görselleri kaldırmayı unutmayın.

Calibre 6’dan itibaren açık ve karanlık mod için özel simgelere sahip olabilirsiniz. Simgenin iki versiyonunu oluşturmanız ve dosyaları -for-dark-theme ve -for-light-theme sonekleriyle adlandırmanız yeterlidir. Örneğin, “karanlık-tema için değiştirildi.png” ve “açık-tema için değiştirildi.png”. Daha sonra calibre geçerli temaya göre uygun simgeyi otomatik olarak kullanacaktır.

Eklentilerle calibre’yi özelleştirmek
calibre oldukça modüler bir tasarıma sahiptir. Calibre’deki hemen hemen tüm işlevler eklentiler biçiminde gelir. Eklentiler dönüşüm için, haber indirmek için (bunlara tarif denir), kullanıcı arayüzünün çeşitli bileşenleri için, farklı cihazlara bağlanmak için, dosyaları calibre’ye eklerken işlemek için kullanılır. Tercihler → Gelişmiş → Eklentiler’e giderek calibre’deki tüm yerleşik eklentilerin tam listesini alabilirsiniz.

Kendi eklentilerinizi yazarak calibre’yi özelleştirebilir ve davranışlarını genişletebilirsiniz. Eklenti mimarisi çok basittir, Calibre’nin işlevselliğini artırmak için kendi eklentilerinizi yazmak rehberine bakabilirsiniz.

Bir eklenti yazdıktan sonra, bunu Mobileread [https://www.mobileread.com/forums/forumdisplay.php?f=237] adresindeki calibre eklentileri forumuna yükleyebilirsiniz ve calibre aracılığıyla kullanılabilir hale gelecektir. yerleşik eklenti güncelleyici.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Calibre’yi özelleştirmek »
- Eklentiler için API belgeleri

Eklentiler için API belgeleri

Güçlü eklentiler oluşturmak için alt sınıf yapılabilir çeşitli soyut temel sınıfları tanımlar. Kullanışlı sınıflar şunlardır:

- Eklenti

- FileTypePlugin

- Metadata eklentileri

- Katalog eklentileri

- Metadata indirme eklentileri

- Dönüştürme eklentileri

- Aygıt sürücüleri

- Kullanıcı arayüzü eylemleri

- Preferences plugins

Eklenti
class calibre.customize.Plugin(plugin_path)[kaynak]

Bir calibre eklentisi. Kullanışlı üyeleri şunları içerir:

- self.installation_type: Eklentinin nasıl kurulduğunu saklar.

- self.plugin_path: Aşağıdakileri içeren ZIP dosyasının yolunu saklar:

içeren zip dosyasına yolu tutar

- self.site_customization: Kullanıcı tarafından girilen bir özelleştirme

karakter dizisini tutar.

Alt sınıflarda ezilmesi gereken metodlar:

- initialize()

- customization_help()

Kullanışlı metodlar:

- temporary_file()

- __enter__()

- load_resources()

supported_platforms = []

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

name = 'Trivial Plugin'

Bu eklentinin adı. Çalışması için Trivial Plugin dışında bir şeye ayarlamalısınız.

version = (1, 0, 0)

Eklentinin 3-lü demet şeklinde sürümü (majör, minör, düzeltme)

description = 'Kesinlikle hiçbir şey yapmaz'

Bu eklentinin ne yaptığını anlatan kısa karakter dizisi

author = 'Bilinmiyor'

Eklentinin yazarı

priority = 1

Bir dosya türü için birden fazla eklenti mevcut olduğunda, eklentiler azalan öncelik sırasına göre çalıştırılır. Daha yüksek önceliğe sahip eklentiler ilk önce çalıştırılacaktır. Mümkün olan en yüksek öncelik “sys.maxsize”dir. Varsayılan öncelik 1’dir.

minimum_calibre_version = (0, 4, 118)

Bu eklentinin gerektirdiği en eski calibre sürümü

installation_type = None

Bu eklentinin kurulma şekli

can_be_disabled = True

False ise, kullanıcının bu eklentiyi kapatma şansı olmayacaktır. Dikkatli kullanın.

type = 'Temel'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

initialize()[kaynak]

Calibre eklentileri ilklendirildiğinde bir defa çağrılır. Eklentiler yeni bir eklenti eklendiğinde her defasında yeniden ilklendirilirler. Ayrıca eklenti bir çalışan süreç içinde çalıştırılırsa, kitap ekleme süreci gibi, eklenti her yeni çalışma süreciyle ilklendirilecektir.

Eklenti ZIP dosyasından kaynakların çıkarılması gibi eklentiye özel herhangi bir başlatma işlemini burada gerçekleştirin. ZIP dosyasının yolu self.plugin_path olarak mevcuttur.

self.site_customization bu noktada kullanılabilir değildir.

config_widget()[kaynak]

Basit karakter dizisi temelli varsayılan özelleştirmeye güvenmek yerine bu metodu ve save_settings() metodunu eklentinizde uygulayarak özel bir yapılandırma iletişim penceresi kullanın.

Bu metod, eğer uygulanırsa, bir QWidget döndürmelidir. Parçacık isteğe bağlı olarak bağımsız değişken almayan bir validate() metoduna sahip olabilir ve kullanıcı Tamama tıklayınca anında çağrılır. Değişiklikler yalnızca metod True döndürürse uygulanır.

Bir sebepten bu aşamada yapılandırma yapamazsanız, iki karakter dizisi içeren bir demet döndürün (ileti, detaylar), bunlar kullanıcıya uyarı penceresi olarak gösterilir ve süreç iptal edilir.

save_settings(config_widget)[kaynak]

Kullanıcı tarafından config_widget ile belirtilen ayarları kaydet.

Parametreler:

config_widget – config_widget() tarafından döndürülen parçacık.

do_user_config(parent=None)[kaynak]

Bu metod bu eklenti için bir yapılandırma iletişim penceresi gösterir. Kullanıcı Tamama tıklarsa True, yoksa False döndürür. Değişiklikler otomatik olarak uygulanır.

load_resources(names)[kaynak]

Bu eklenti bir ZIP dosyasıyla gelirse (kullanıcı tarafından eklenmiş eklenti), bu metod ZIP dosyasından kaynakları yüklemenizi sağlar.

Örneğin bir resmi yüklemek için:

pixmap = QPixmap()
pixmap.loadFromData(self.load_resources(['images/icon.png'])['images/icon.png'])
icon = QIcon(pixmap)

Parametreler:

names – Ayırıcı olarak / kullanılarak ZIP dosyasındaki kaynaklara giden yolların listesi

Dönüşler:

{name: file_contents} biçiminde bir sözlük. ZIP dosyasında bulunmayan adlar sözlükte de yer almayacaktır.

customization_help(gui=False)[kaynak]

Bu eklentinin nasıl özelleştirileceği konusunda yardım veren bir metin. Varsayılan olarak NotImplementedError yükseltilir, bu da eklentinin özelleştirmeye ihtiyaç duymadığı anlamına gelir.

Bu metodu alt sınıflarınızda yeniden uyguluyorsanız, kullanıcıdan bu eklenti için özelleştirme olarak kullanılacak bir karakter dizisi girmesi istenir. Özelleştirme karakter dizisi self.site_customization olarak kullanılabilir olacaktır.

Site özelleştirmesi herşey olabilir, örneğin kullanıcının bilgisayarındaki bir ikiliğin yolu.

Parametreler:

gui – True ise HTML yardımı döndür, yoksa düz metin yardımı.

temporary_file(suffix)[kaynak]

Sistemde geçici bir dosya olan dosya-benzeri nesneyi döndür. Bu dosya kapandıktan sonra bile kullanılabilir olur ve yalnızca yorumlayıcı kapandığında silinir. Oluşturulan geçici dosyanın tam yoluna erişmek için dönen nesnenin name metodunu kullanın.

Parametreler:

suffix – Geçici dosyanın son eki.

cli_main(args)[kaynak]

Bu metod eklentinizin komut satırı arayüzü için ana giriş noktasıdır. Kullanıcı calibre-debug -r “Eklenti İsmi” girdiğinde çağrılır. Bağımsız değişkenler args değişkenine mevcuttur.

FileTypePlugin
class calibre.customize.FileTypePlugin(plugin_path)[kaynak]

Temeller: Plugin

Belirli bir dosya türü kümesiyle ilişkili eklenti.

file_types = {}

Bu eklentinin çalıştırılması gereken dosya türleri kümesi. Tüm dosya türleri için ‘*’ kullanın. Örneğin: {'lit', 'mobi', 'prc'}

on_import = False

True ise, bu eklenti kitaplar veri tabanına eklendiğinde çalışır

on_postimport = False

True ise bu eklenti kitaplar veritabanına eklendikten sonra çalıştırılır. Bu durumda eklentinin postimport ve postadd yöntemleri çağrılır.

on_postconvert = False

True ise bu eklenti kitap dönüştürüldükten sonra çalıştırılır. Bu durumda eklentinin postconvert yöntemi çağrılır.

on_postdelete = False

True ise bu eklenti veritabanından bir kitap dosyası silindikten sonra çalıştırılır. Bu durumda eklentinin silme sonrası yöntemi çağrılır.

on_preprocess = False

True ise, bu eklenti dönüştürme öncesi çalışır

on_postprocess = False

True ise, bu eklenti dönüştürme çıktı eklentisi tarafından üretilen son dosyanın dönüştürmesi sonrası çalışır.

type = 'Dosya türü'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

run(path_to_ebook)[kaynak]

Eklentiyi çalıştırın. Alt sınıflarda uygulanmalıdır. E-kitapta yapılması gereken değişiklikleri yapmalı ve değiştirilen e-kitabın mutlak yolunu döndürmelidir. Herhangi bir değişiklik gerekmiyorsa, orijinal e-kitabın yolunu döndürmelidir. Bir hatayla karşılaşılırsa bir İstisna oluşturulmalıdır. Varsayılan uygulama yalnızca orijinal e-kitabın yolunu döndürür. Orijinal dosyanın yolunun (herhangi bir dosya türü eklentisi çalıştırılmadan önce self.original_path_to_file olarak mevcut olduğunu) unutmayın.

Değiştirilen e-kitap dosyası temporary_file() yöntemiyle oluşturulmalıdır.

Parametreler:

path_to_ebook – E-kitabın mutlak yolu.

Dönüşler:

Değiştirilen e-kitabın mutlak yolu.

postimport(book_id, book_format, db)[kaynak]

İçe aktarım sonrası denir, yani kitap dosyası veritabanına eklendikten sonra. Bunun, kitap kaydı ilk kez oluşturulduğunda çağrılan :meth:`postadd`den farklı olduğunu unutmayın. Bu yöntem, bir kitap kaydına yeni bir dosya eklendiğinde çağrılır. Yeni eklenen dosyanın içeriğine göre kitap kaydını değiştirmek için kullanışlıdır.

Parametreler:

- book_id – Eklenen kitabın veri tabanı kimliği.

- book_format – Eklenen kitabın dosya türü.

- db – Kitaplık veri tabanı.

postconvert(book_id, book_format, db)[kaynak]

Dönüştürme sonrası olarak adlandırılır, yani dönüştürme çıktı kitabı dosyası veritabanına eklendikten sonra. Bunun bir kitap eklendikten sonra değil, yalnızca bir dönüşümden sonra çalıştırıldığını unutmayın. Yeni eklenen dosyanın içeriğine göre kitap kaydını değiştirmek için kullanışlıdır.

Parametreler:

- book_id – Eklenen kitabın veri tabanı kimliği.

- book_format – Eklenen kitabın dosya türü.

- db – Kitaplık veri tabanı.

postdelete(book_id, book_format, db)[kaynak]

Silme sonrası denir, yani kitap dosyası veritabanından silindikten sonra. Bir kitap kaydı silindiğinde çalıştırılmadığını, yalnızca kitaptan bir veya daha fazla format silindiğinde çalıştırıldığını unutmayın. Silinen dosyanın formatına göre kitap kaydını değiştirmek için kullanışlıdır.

Parametreler:

- book_id – Eklenen kitabın veri tabanı kimliği.

- book_format – Eklenen kitabın dosya türü.

- db – Kitaplık veri tabanı.

postadd(book_id, fmt_map, db)[kaynak]

Post add olarak adlandırılır, yani veri tabanına bir kitap eklendikten sonra. Bunun, tek bir kitap dosyası bir kitaba eklendikten sonra çağrılan :meth:`postimport`tan farklı olduğunu unutmayın. postadd() yalnızca muhtemelen birden fazla kitap dosyası içeren bir kitap kaydının tamamı ilk kez oluşturulduğunda çağrılır. Kitap calibre’ye ilk eklendiğinde veri tabanındaki kitap kaydını değiştirmek istiyorsanız bu kullanışlıdır.

Parametreler:

- book_id – Eklenen kitabın veri tabanı kimliği.

- fmt_map – Dosya biçiminin, dosya biçiminin eklendiği yol ile haritası. Bazen dosyalar akış olarak eklendiğinden, bunun mevcut bir dosyaya işaret edebileceğini veya etmeyebileceğini unutmayın. Bu durumda sahte bir değer veya var olmayan bir yol olabilir.

- db – Kitaplık veri tabanı

Metadata eklentileri

class calibre.customize.MetadataReaderPlugin(*args, **kwargs)[kaynak]

Temeller: Plugin

Bir dosya türü kümesinden metadata okumayı uygulayan bir eklenti.

file_types = {}

Bu eklentinin çalıştırılması gereken dosya türleri kümesi. Örneğin: set(['lit', 'mobi', 'prc'])

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

version = (7, 10, 0)

Eklentinin 3-lü demet şeklinde sürümü (majör, minör, düzeltme)

author = 'Kovid Goyal'

Eklentinin yazarı

type = 'Metaveri okuyucu'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

get_metadata(stream, type)[kaynak]

Akış tarafından temsil edilen dosyanın (okumayı destekleyen dosya benzeri bir nesne) meta verilerini döndürün. Giriş verilerinde bir hata olduğunda bir istisna oluşturun.

Parametreler:

type – Dosya türü. file_types içindeki girdilerden biri olduğu garanti edilir.

Dönüşler:

A :sınıf:`calibre.ebooks.metadata.book.Metadata` nesnesi

class calibre.customize.MetadataWriterPlugin(*args, **kwargs)[kaynak]

Temeller: Plugin

Bir dosya türü kümesinden metadata okumayı uygulayan bir eklenti.

file_types = {}

Bu eklentinin çalıştırılması gereken dosya türleri kümesi. Örneğin: set(['lit', 'mobi', 'prc'])

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

version = (7, 10, 0)

Eklentinin 3-lü demet şeklinde sürümü (majör, minör, düzeltme)

author = 'Kovid Goyal'

Eklentinin yazarı

type = 'Metaveri yazıcı'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

set_metadata(stream, mi, type)[kaynak]

Akış tarafından temsil edilen dosya için meta verileri ayarlayın (okumayı destekleyen dosya benzeri bir nesne). Giriş verilerinde bir hata olduğunda bir istisna oluşturun.

Parametreler:

- type – Dosya türü. file_types içindeki girdilerden biri olduğu garanti edilir.

- mi – A :sınıf:`calibre.ebooks.metadata.book.Metadata` nesnesi

Katalog eklentileri

class calibre.customize.CatalogPlugin(plugin_path)[kaynak]

Temeller: Plugin

Bir katalog üreteci uygulayan eklenti.

file_types = {}

Bu eklentinin çalıştırılması gereken çıktı dosyası türü. Örneğin: ‘epub’ veya ‘xml’

type = 'Katalog üreteci'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

cli_options = []

Bu eklentiye özel CLI ayrıştırıcı seçenekleri, “namedtuple” “Option” olarak bildirildi:

koleksiyonlardan içe aktar Nametuple Option = nametuple(‘Option’, ‘option, default, dest, help’) cli_options = [Option(’–catalog-title’, default = ‘Kataloğum’, dest = ‘catalog_title’, help = (_(‘Oluşturulan kataloğun başlığı. nDefault:’) + “ ‘” + ‘%default’ + “’”))] calibre.db.cli.cmd_catalog:option_parser() içinde ayrıştırılan cli_options

initialize()[kaynak]

Eklenti yerleşik değilse, eklentinin .ui ve .py dosyalarını ZIP dosyasından $TMPDIR’e kopyalayın. Sekme dinamik olarak oluşturulacak ve calibre.gui2.dialogs.catalog.py:Catalog’daki Katalog Seçenekleri iletişim kutusuna eklenecektir.

run(path_to_output, opts, db, ids, notification=None)[kaynak]

Eklentiyi çalıştır. Alt sınıflarda uygulanmalıdır. file_types’da belirtilen biçimde kataloğu üretmelidir, ve üretilen katalog dosyasına mutlak yolu döndürmelidir. Bir hataya rastlanırsa bir Exception yükseltmelidir.

Üretilen katalog dosyası temporary_file() metoduyla oluşturulmalıdır.

Parametreler:

- path_to_output – Üretilen katalog dosyasına mutlak yol.

- opts – Anahtar kelime değişkenlerinden oluşan bir sözlük

- db – Bir LibraryDatabase2 nesnesi

Metadata indirme eklentileri
class calibre.ebooks.metadata.sources.base.Source(*args, **kwargs)[kaynak]

Temeller: Plugin

type = 'Metadata kaynağı'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

author = 'Kovid Goyal'

Eklentinin yazarı

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

capabilities = frozenset({})

Bu eklenti tarafından desteklenen yetenek kümesi. Kullanışlı yetenekler: ‘identify’, ‘cover’

touched_fields = frozenset({})

Bu eklenti tarafından tanımlama aşamasında indirilebilme potansiyeline sahip metadata alanları listesi

has_html_comments = False

Eklentiniz HTML biçimli yorumlar döndürüyorsa bunu True olarak ayarlayın

supports_gzip_transfer_encoding = False

Bunu True olarak ayarlamak, tarayıcı nesnesinin gzip aktarım kodlamasını desteklediğini belirteceği anlamına gelir. Bu, indirmeleri hızlandırabilir ancak öncelikle kaynağın gerçekten gzip aktarım kodlamasını doğru şekilde desteklediğinden emin olun.

ignore_ssl_errors = False

Bu kaynağa bağlanırken HTTPS sertifika hatalarını yok saymak için bunu True olarak ayarlayın.

cached_cover_url_is_reliable = True

Önbelleğe alınan kapak URL’leri bazen güvenilmez olabilir (yani indirme işlemi başarısız olabilir veya döndürülen resim sahte olabilir). Bu kaynakta sıklıkla durum böyleyse Yanlış olarak ayarlayın

options = ()

Option nesneleri listesi. Bu eklenti için otomatik olarak yapılandırma parçacığı inşa etmek için kullanılacaklar

config_help_message = None

Bu eklenti için ayar penceresinin üstünde gösterilecek bir karakter dizisi

can_get_multiple_covers = False

True ise bu kaynak verilen sorgu için birden çok kapak döndürebilir

auto_trim_covers = False

True olarak ayarlıysa bu eklenti tarafından indirilen kapaklar otomatik olarak düzeltilir.

prefer_results_with_isbn = True

True olarak ayarlanırsa, ve bu kaynak bir sorgu için bazılarında ISBN’ler olan bazılarında olmayan birden çok sonuç döndürüyorsa, ISBN olmayan sonuçlar göz ardı edilir

is_configured()[kaynak]

Eklentinizin kullanılmadan önce yapılandırılması gerekiyorsa False döndür. Örneğin bir kullanıcı adı/parola/API anahtarına ihtiyaç duyuyor olabilir.

customization_help()[kaynak]

Bu eklentinin nasıl özelleştirileceği konusunda yardım veren bir metin. Varsayılan olarak NotImplementedError yükseltilir, bu da eklentinin özelleştirmeye ihtiyaç duymadığı anlamına gelir.

Bu metodu alt sınıflarınızda yeniden uyguluyorsanız, kullanıcıdan bu eklenti için özelleştirme olarak kullanılacak bir karakter dizisi girmesi istenir. Özelleştirme karakter dizisi self.site_customization olarak kullanılabilir olacaktır.

Site özelleştirmesi herşey olabilir, örneğin kullanıcının bilgisayarındaki bir ikiliğin yolu.

Parametreler:

gui – True ise HTML yardımı döndür, yoksa düz metin yardımı.

config_widget()[kaynak]

Basit karakter dizisi temelli varsayılan özelleştirmeye güvenmek yerine bu metodu ve save_settings() metodunu eklentinizde uygulayarak özel bir yapılandırma iletişim penceresi kullanın.

Bu metod, eğer uygulanırsa, bir QWidget döndürmelidir. Parçacık isteğe bağlı olarak bağımsız değişken almayan bir validate() metoduna sahip olabilir ve kullanıcı Tamama tıklayınca anında çağrılır. Değişiklikler yalnızca metod True döndürürse uygulanır.

Bir sebepten bu aşamada yapılandırma yapamazsanız, iki karakter dizisi içeren bir demet döndürün (ileti, detaylar), bunlar kullanıcıya uyarı penceresi olarak gösterilir ve süreç iptal edilir.

save_settings(config_widget)[kaynak]

Kullanıcı tarafından config_widget ile belirtilen ayarları kaydet.

Parametreler:

config_widget – config_widget() tarafından döndürülen parçacık.

get_author_tokens(authors, only_first_author=True)[kaynak]

Bir yazar listesi al ve bir AND arama sorgusu için kulanışlı olacak jeton listesi döndür. Bu fonksiyon ilk ad göbek adı soyad sırasında jeton döndürmeye çalışır, yazar isminde bir virgül varsa, ismin soyad, diğer ad biçiminde olduğunu farz eder.

get_title_tokens(title, strip_joiners=True, strip_subtitle=False)[kaynak]

Bir başlık al ve bir AND arama sorgusu için kullanışlı jeton listesi döndür. Bağlaç(isteğe bağlı) ve noktalama işaretleri hariç.

split_jobs(jobs, num)[kaynak]

Bir iş listesini olabildiğince eşit şekilde en fazla num gruba ayır

test_fields(mi)[kaynak]

self.touched_files’dan mi nesnesinde null olan ilk alanı döndür

clean_downloaded_metadata(mi)[kaynak]

Bu metodu eklentinizin tanımlama metodunda Metadata nesnesini result_queue’ye koymadan önce metadata’yı normalleştirmek için çağırın. Tabi ki metadata kaynağınıza uygun özel bir algoritma da kullanabilirsiniz.

get_book_url(identifiers)[kaynak]

3’lü bir grup veya Hiçbiri döndürün. 3’lü grup şu biçimdedir: (tanımlayıcı_tipi, tanımlayıcı_değeri, URL). URL, bu kaynaktaki tanımlayıcılar tarafından tanımlanan kitabın URL’sidir. tanımlayıcı_türü, tanımlayıcı_değer URL’ye karşılık gelen tanımlayıcıyı belirtir. Bu URL’ye tarayıcı kullanan bir kişi tarafından göz atılabilir olmalıdır. Kullanıcının bu kaynaktaki kitap sayfasını kolayca ziyaret edebilmesi için tıklanabilir bir bağlantı sağlanması amaçlanmaktadır. Hiçbir URL bulunamazsa Yok değerini döndürün. Bu yöntemin hızlı ve tutarlı olması gerekir; bu nedenle, yalnızca URL’yi bilinen bir şema verilen tanımlayıcılardan oluşturmak mümkünse uygulayın.

get_book_url_name(idtype, idval, url)[kaynak]

get_book_url()’nin döndürdüğü değerden okunabilir bir isim döndür.

get_book_urls(identifiers)[kaynak]

Bu kitap için birden fazla URL döndürmek istiyorsanız bu yöntemi geçersiz kılın. 3’lü demetlerin listesini döndürün. Varsayılan olarak bu yöntem yalnızca get_book_url() öğesini çağırır.

get_cached_cover_url(identifiers)[kaynak]

Tanımlayıcılar sözlüğü tarafından tanımlanan kitabın önbelleğe alınmış kapak URL’sini döndürün veya böyle bir URL yoksa Yok’u seçin.

Bu metodun yalnızca doğrulanmış URL’ler döndürmesi gerektiğine dikkat edin, örn. genel kapak resmi veya bulunamadı hatasına yol açacak URL’ler olmamalı.

id_from_url(url)[kaynak]

Bir URL’yi ayrıştırın ve formun bir demetini döndürün: (tanımlayıcı_türü, tanımlayıcı_değer). URL, meta veri kaynağının modeliyle eşleşmiyorsa Yok değerini döndürün.

identify_results_keygen(title=None, authors=None, identifiers={})[kaynak]

Bir arama sorgusu (başlık, yazarlar, tanımlayıcılar) alarak Metadata nesnelerini sıralamaya yarayacak bir anahtar üretmeye yarayan bir fonksiyon döndür.

Bu anahtarlar identify() çağrısı sonucunu sıralamaya yarar.

Varsayılan algoritma detayları için bknz InternalMetadataCompareKeyGen. Varsayılan algoritma uygun değilse bu fonksiyonu tekrar uygulayın.

identify(log, result_queue, abort, title=None, authors=None, identifiers={}, timeout=30)[kaynak]

Bir kitabı Başlığı/Yazar/ISBN/vb. ile tanımlayın.

Tanımlayıcı(lar) belirtilmiş ve bir eşleşme bulunamamışsa ve bu metadata kaynağı ilgili tüm tanımlayıcılar kaydetmemişse (örneğin, bir kitabın tüm ISBN’leri), bu metod sadece başlık ve yazar (belirtildiklerini farz ederek) ile tekrar denemelidir.

Bu metadata kaynağı kapak da sağlıyorsa, kapağın URL’si kapak getirme API’si ile tekrar tekrar aynı ISBN/özel tanımlayıcı çağrısı yapıldığında getirilmek zorunda kalınmasın diye zulalanmalıdır. Zulalama API’sini bunun için kullanın.

Bu metod tarafından result_queue’ya konan her Metadata nesnesinin bu sorgu için metadata kaynağınca döndürülen sonuçların sırasını belirten bir tamsayı olan source_relevance özniteliği olmalıdır. Bu tam sayı compare_identify_results() tarafından kullanılacaktır. Sıra önemli değilse, tüm sonuçlar için sıfır olarak ayarlayın.

Metadata nesnesi result_queue’ye yerleştirilmeden önce tüm kapak/ISBN eşleme bilgilerinin önbelleğe alındığından emin olun.

Parametreler:

- log – Bir sistem günlüğü nesnesi, hata ayıklama bilgisi/hatası çıktılamak için kullanın

- result_queue – Bir sonuç Kuyruğu, sonuçlar buna koyulmalı. Her sonuç bir Metadata nesnesidir

- abort – Eğer abort.is_set() True döndürürse, işlemeyi durdur ve en kısa sürede dön

- title – Kitabın başlığı, None olabilir

- authors – Kitap için yazarlar listesi, None olabilir

- identifiers – Diğer tanımlayıcıları içeren bir sözlük, en yaygın şekliyle {‘isbn’:’1234…’}

- timeout – Saniye cinsinden zaman aşımı, hiçbir ağ isteği zaman aşımından uzun sürmemeli.

Dönüşler:

Hataya rastlanmadıysa None, aksi halde hatanın kullanıcıya gösterilebilecek evrensel kodda temsili

download_cover(log, result_queue, abort, title=None, authors=None, identifiers={}, timeout=30, get_best_cover=False)[kaynak]

Bir kapak indir ve result_queue’ya koy. Parametreler identify() metodundakiyle aynı anlamdadırlar. (self, cover_data)’yı result_queue’ya koy.

Bu metod verimlilik için uygun olan her durumda zulalanmış kapak URL’lerini kullanmalıdır. Zulalı veri olmadığında, çoğu eklenti identify’ı çağırır ve onun sonuçlarını kullanır.

get_best_cover parametresi True ise ve bu eklenti birden çok kapak alabiliyorsa, yalnızca “en iyi” olanı almalı.

class calibre.ebooks.metadata.sources.base.InternalMetadataCompareKeyGen(mi, source_plugin, title, authors, identifiers)[kaynak]

Bir arama sorgusu verildiğinde, Metadata nesnelerinin uygunluğunun karşılaştırması için bir sıralama anahtarı üret. Bu yalnızca aynı metadata kaynağından gelen sonuçları karşılaştırmak için kullanılır, farklı kaynaklar arasında değil.

Arama anahtarı artan sırada sıralamanın azalan uygunluk sırasında sıralama olduğundan emin olur.

Algoritma:

- Sorguda olduğu gibi en az bir tanımlayıcısı olan sonuçları tercih et

- Zulalanmış kapak URL’si olan sonuçları tercih et

- Kullanılabilir alanların hepsi dolu olan sonuçları tercih et

- Mevcut kullanıcı arayüzüyle aynı dilde olan sonuçları tercih et

- Sorguyla başlığı birebir eşleşen sonuçları tercih et

- Daha uzun yorumlu sonuçları tercih et (%10 uzunluktan fazlası)

- Sonucun uygunluğunu metadata kaynağının aramasından gelen rapor olarak kullan

motor

Dönüştürme eklentileri
class calibre.customize.conversion.InputFormatPlugin(*args)[kaynak]

Temeller: Plugin

InputFormatPlugins bir belgeyi HTML+OPF+CSS+vs. ye dönüştürmekle sorumludurlar. Dönüştürmenin sonuçları UTF-8 ile kodlanmış olmak zorundadır. Esas olay convert() içinde gerçekleşir.

type = 'Girdi dönüşümü'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

can_be_disabled = False

False ise, kullanıcının bu eklentiyi kapatma şansı olmayacaktır. Dikkatli kullanın.

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

file_types = {}

Bu eklentinin çalışacağı dosya türü kümesi. Örneğin: set(['azw', 'mobi', 'prc'])

is_image_collection = False

True ise, bu girdi eklentisi HTML dosyası başına bir resim koleksiyonu üretir. Bu, dönüştürme metodunda, girdi dosyaları hem resim koleksiyonları hem resim olmayan koleksiyonlar olabiliyorsa dinamik olarak ayarlanabilir. True olarak ayarlarsanız, resim listesi döndüren get_images() metodunu uygulamalısınız.

core_usage = 1

Bu eklenti tarafından kullanılan CPU çekirdeği sayısı. -1 değeri, mevcut tüm çekirdekleri kullandığı anlamına gelir

for_viewer = False

True olarak ayarlanırsa, girdi eklentisi çıktısını görüntülemeye uygun hale getirmek için özel işlemden geçirir

output_encoding = 'utf-8'

Bu girdi eklentisinin dosyaları içinde oluşturduğu kodlama. None değeri kodlamanın belirtilmemiş olduğunu ve bağımsız şekilde algılanması gerektiğini belirtir

common_options = {<calibre.customize.conversion.OptionRecommendation object>}

Tüm girdi biçim eklentilerince paylaşılan seçenekler. Alt sınıflarda ezmeyin. Bunun yerine options kullanın. Her seçenek bir OptionRecommendation örneği olmalıdır.

options = {}

Bu eklentinin davranışını özelleştirecek seçenekler. Tüm seçenekler OptionRecommendation örneği olmalıdırlar.

recommendations = {}

(seçenek_ismi, önerilen_değer, önerilen_seviye) biçiminde 3’lü demet kümesi

get_images()[kaynak]

Bu eklenti bir resim koleksiyonunu destekliyorsa, resimlere mutlak yol listesini döndür. Resim listesi omurga ve İçindekiler ile aynı sıradadır.

convert(stream, options, file_ext, log, accelerators)[kaynak]

Bu yöntemin alt sınıflarda uygulanması gerekir. Oluşturulan OPF dosyasının veya OEBBook örneğinin yolunu döndürmelidir. Tüm çıktı geçerli klasörde bulunmalıdır. Bu eklenti geçerli klasörün dışında dosyalar oluşturursa, bu yöntem geri dönmeden önce bunların silinmesi/silinmek üzere işaretlenmesi gerekir.

Parametreler:

- stream – Girdi dosyasını içeren dosya benzeri nesne.

- options – Dönüştürme sürecini özelleştirecek seçenekler. Bu eklentice bildirilen tüm seçeneklere karşılık gelen özniteliklere sahip olacağı garantidir. Ek olarak, tamamlayıcı değerleri sıfırdan yukarı doğru alan ayrıntılı bir özniteliği olacaktır. Yüksek sayılar daha ayrıntılı demektir. Kullanışlı başka bir öznitelik de calibre.customize.profiles.InputProfile örneği olan input_profile özniteliğidir.

- file_ext – Girdinin uzantısı (. olmadan). Bu eklentice desteklenen dosya_türlerinden biri olması garantidir.

- log – calibre.utils.logging.Log nesnesi. Tüm çıktı bu nesneyi kullanmalı.

- accelarators – Girdi eklentisinin kolayca alabileceği, dönüştürmenin sonraki aşamalarını hızlandıracak olan çeşitli bilgileri içeren sözlük.

postprocess_book(oeb, opts, log)[kaynak]

Girdi eklentisinin kitap ayrıştırıldıktan sonra işlem yapmasına izin vermek için çağrılır.

specialize(oeb, opts, log, output_fmt)[kaynak]

Girdi eklentisinin ayrıştırılan kitabı belli çıktı biçimine özelleştirmesine izin vermek için çağrılır. Ayrıştırılan kitaba herhangi dönüştürme yapılmadan önce postprocess_book’dan sonra çağrılır.

gui_configuration_widget(parent, get_option_by_name, get_option_help, db, book_id=None)[kaynak]

Bu eklentiyi calibre grafik arayüzünde yapılandırmaya yarayan pencereyi oluşturmak için çağrılır. Pencere PluginWidget sınıfından olmalıdır. Örnekler için dahili girdi eklentilerine göz atın.

class calibre.customize.conversion.OutputFormatPlugin(*args)[kaynak]

Temeller: Plugin

OutputFormatPlugins, bir OEB belgesini (OPF+HTML) çıktı e-kitabına dönüştürmekten sorumludur.

OEB belgesinin UTF-8 ile kodlanmış olduğu varsayılabilir. Ana olay convert() içinde gerçekleşir.

type = 'Çıktı dönüşümü'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

can_be_disabled = False

False ise, kullanıcının bu eklentiyi kapatma şansı olmayacaktır. Dikkatli kullanın.

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

file_type = None

Bu eklentinin çıktıladığı dosya türü (noktasız son ek)

common_options = {<calibre.customize.conversion.OptionRecommendation object>}

Tüm girdi biçim eklentilerince paylaşılan seçenekler. Alt sınıflarda ezmeyin. Bunun yerine options kullanın. Her seçenek bir OptionRecommendation örneği olmalıdır.

options = {}

Bu eklentinin davranışını özelleştirecek seçenekler. Tüm seçenekler OptionRecommendation örneği olmalıdırlar.

recommendations = {}

(seçenek_ismi, önerilen_değer, önerilen_seviye) biçiminde 3’lü demet kümesi

property description

str(object=’’) -> str str(bytes_or_buffer[, kodlama[, hatalar]]) -> str

Verilen nesneden yeni bir dize nesnesi oluşturun. Kodlama veya hatalar belirtilirse, nesnenin, verilen kodlama ve hata işleyicisi kullanılarak kodu çözülecek bir veri arabelleğini açığa çıkarması gerekir. Aksi halde, object.__str__() (tanımlanmışsa) veya repr(object) sonucunu döndürür. kodlama varsayılan olarak sys.getdefaultencoding() şeklindedir. hatalar varsayılan olarak ‘katı’dır.

convert(oeb_book, output, input_plugin, opts, log)[kaynak]

Oeb_book içeriğini (bu, calibre.ebooks.oeb.OEBBook örneğinin bir örneğidir) çıktı tarafından belirtilen dosyaya işleyin.

Parametreler:

- output – Ya nesneye benzer bir dosya ya da bir dize. Bu bir dize ise, var olan veya olmayan bir klasörün yoludur. Çıktı eklentisi çıktısını bu klasöre yazmalıdır. Dosya benzeri bir nesne ise, çıktı eklentisi çıktısını dosyaya yazmalıdır.

- input_plugin – Dönüştürme boru hattının başında kullanılan girdi eklentisi.

- opts – Dönüştürme seçenekleri. Bu eklentinin SeçenekÖnerilerine karşılık gelen öznitelikleri içermesi garantidir.

- log – Günlük kaydedici. Hata ayıklama/bilgi vs. iletilerini bunu kullanarak yazdırın.

specialize_options(log, opts, input_fmt)[kaynak]

Dönüşüm hattı tarafından kullanıldığı şekliyle, dönüştürme seçeneklerinin değerlerini değiştirmek için kullanılabilir.

specialize_css_for_output(log, opts, item, stylizer)[kaynak]

CSS düzleştirme işlemi sırasında CSS’de değişiklik yapmak için kullanılabilir.

Parametreler:

- item – İşlenen öğe (HTML dosyası)

- stylizer – Öğe için düzleştirilmiş biçemleri içeren Stylizer nesnesi. Herhangi bir öğenin biçemini stylizer.style(öğe) ile alabilirsiniz.

gui_configuration_widget(parent, get_option_by_name, get_option_help, db, book_id=None)[kaynak]

Bu eklentiyi calibre GUI’sinde yapılandırmak için kullanılan parçacığı oluşturmak için çağrılır. Parçacık bir PluginWidget sınıfı örneği olmalıdır. Örnekler için dahili çıktı eklentilerine göz atın.

Aygıt sürücüleri

Tüm aygıt sürücüleri için taban sınıf DevicePlugin dir. Ancak aygıtınız kendisini işletim sistemine USBMS sürücüsü olarak gösteriyorsa bu tür cihazları destekeleyecek her türlü mantığı uyguladığından USBMS sınıfını kullanmalısınız.

class calibre.devices.interface.DevicePlugin(plugin_path)[kaynak]

Temeller: Plugin

Bir e-kitap okuyucuyla iletişim kuran arka uçlar tarafından uygulanması gereken arayüzü tanımlar.

type = 'Cihaz arabirimi'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

FORMATS = ['lrf', 'rtf', 'pdf', 'txt']

Desteklenen biçimlerin sıralı listesi

VENDOR_ID = 0

VENDOR_ID bir tam sayı, tamsayı listesi veya sözlük olabilir. Bir sözlükse, sözlüklerin sözlüğü olmalıdır, şu şekilde:

{
integer_vendor_id : { product_id : [list of BCDs], ... },
...
}

PRODUCT_ID = 0

Bir tam sayı veya tam sayı listesi

BCD = None

BCD BCD tabanlı aygıtlar arasında ayrı kalmaması için None olabilir, veya bu sürücü tarafından desteklenen tüm aygıtların BCD numaralarından oluşan liste olabilir.

THUMBNAIL_HEIGHT = 68

Küçük resimlerin aygıttaki yüksekliği

THUMBNAIL_COMPRESSION_QUALITY = 75

Küçük resimler için sıkıştırma kalitesi. Daha az sıkıştırma kusuruyla daha kaliteli küçük resimler elde etmek için bunu 100’e yaklaştırın. Elbette küçük resimler de büyüyor.

WANTS_UPDATED_THUMBNAILS = False

Aygıt sync_booklists sırasında kapak küçük resimlerini güncellemeye izin veriyorsa bunu True olarak ayarlayın. True olarak ayarlamak kitap eşleştirme sırasında device.py’ye kapak küçük resimlerini güncellemesini söyler

CAN_SET_METADATA = ['title', 'authors', 'collections']

Kitaplardaki metadata’nın GUI ile ayarlanıp ayarlanamayacağı.

CAN_DO_DEVICE_DB_PLUGBOARD = False

Aygıtın device_db metadata santralini ele alıp alamayacağı

path_sep = '/'

Bu aygıttaki kitaplara olan yollar için yol ayracı

icon = 'reader.png'

Bu aygıt için simge

UserAnnotation

alias of Annotation

OPEN_FEEDBACK_MESSAGE = None

GUI, durum çubuğunda Hiçbiri değilse bunu bir mesaj olarak görüntüler. Açılışın uzun zaman alabileceği durumlarda kullanışlıdır

VIRTUAL_BOOK_EXTENSIONS = frozenset({})

Aygıtta “sanal kitap” olan ve bu yüzden kitaplıkta görüntülenemeyen/kaydedilemeyen/eklenemeyen eklentiler kümesi. Örneğin: frozenset(['kobo'])

VIRTUAL_BOOK_EXTENSION_MESSAGE = None

Sanal kitap uzantıları için kullanıcıya gösterilecek mesaj.

NUKE_COMMENTS = None

Kitabın aygıta yollanan kopyasında yorumlar tamamen silinsin mi. None değilse bunun yorumların değiştirileceği kısa bir metin olması gerekir.

MANAGES_DEVICE_PRESENCE = False

If True indicates that this driver completely manages device detection, ejecting and so forth. If you set this to True, you must implement the detect_managed_devices and debug_managed_device_detection methods. A driver with this set to true is responsible for detection of devices, managing a blacklist of devices, a list of ejected devices and so forth. calibre will periodically call the detect_managed_devices() method and if it returns a detected device, calibre will call open(). open() will be called every time a device is returned even if previous calls to open() failed, therefore the driver must maintain its own blacklist of failed devices. Similarly, when ejecting, calibre will call eject() and then assuming the next call to detect_managed_devices() returns None, it will call post_yank_cleanup().

SLOW_DRIVEINFO = False

True olarak ayarlandıysa, calibre kitap listeleri yüklendikten sonra sürücü bilgisi almak için get_driveinfo() çağıracaktır.

ASK_TO_ALLOW_CONNECT = False

True olarak ayarlandıysa, calibre kullanıcıya aygıt ilk kez algılandığında aygıtı calibre ile yönetip isteyip istemediğini sorar. Bunu True olarak ayarlarsanız get_device_uid() ve ignore_connected_device() ve get_user_blacklisted_devices() ve set_user_blacklisted_devices() uygulamalısınız

user_feedback_after_callback = None

Bazı geri çağrılar çalıştırıldıktan sonra (şu an yalnızca upload_books) calibre’nin bir ileti penceresi açmasını sağlamak için bunu {‘title’:başlık, ‘msg’:ileti, ‘det_msg’:detaylı_ileti} biçiminde bir sözlük olarak ayarlayın. Kullanıcıyı bir sürü ileti ile sıkmamaya dikkat edin. Bu değişken her geri çağrıdan sonra kontrol edilir, yalnızca gerçekten ihtiyacınız olduğunda ayarlayın.

classmethod get_open_popup_message()[kaynak]

GUI displays this as a non-modal popup. Should be an instance of OpenPopupMessage

is_usb_connected(devices_on_system, debug=False, only_presence=False)[kaynak]

Bu eklenti ile ele alınan bir aygıt hali hazırda bağlıysa True, device_info döndür.

Parametreler:

devices_on_system – Hali hazırda bağlı olan aygıtlar

detect_managed_devices(devices_on_system, force_refresh=False)[kaynak]

Yalnızca MANAGES_DEVICE_PRESENCE True ise çağrılır.

Bu sürücünün ele alabileceği aygıtları tara. Bir aygıt bulunursa aygıt nesnesi döndürmelidir. Bu nesne open() metoduna connected_device olarak geçirilir. Aygıt bulunamazsa, None döndürür. Döndürülen nesne herşey olabilir, calibre bu nesneyi kullanmaz, sadece open()’a geçirilir.

Bu metod GUI tarafından aralıklarla çağrılır, fazla kaynak tüketmediğinden emin olun. Sistemi tekrar tekrar taramaktan kaçınmak için bir zula kullanın.

Parametreler:

- devices_on_system – Sistemde bulunan USB aygıtları kümesi.

- force_refresh – True ise ve sürücü tekrar taramalardan kaçınmak için zula kullanıyorsa, zula temizlenmelidir.

debug_managed_device_detection(devices_on_system, output)[kaynak]

Yalnızca MANAGES_DEVICE_PRESENCE True ise çağrılır.

Sistemde algılanan sürücüler hakkında bilgiyi dosya benzeri bir nesne olan çıktıya yazmalıdır.

Bir aygıt algılanmış ve başarıyla açılmışsa True, aksi halde False döndürmelidir.

reset(key='-1', log_packets=False, report_progress=None, detected_device=None)[kaynak]

Parametreler:

- key – Aygıtın kilidini açacak tuş

- log_packets – True ise aygıta giden/aygıttan gelen paket akışı kaydedilir

- report_progress – Function that is called with a % progress (number between 0 and 100) for various tasks. If it is called with -1 that means that the task does not have any progress information

- detected_device – Aygıt tarayıcıdan gelen aygıt bilgisi

can_handle_windows(usbdevice, debug=False)[kaynak]

Optional method to perform further checks on a device to see if this driver is capable of handling it. If it is not it should return False. This method is only called after the vendor, product ids and the bcd have matched, so it can do some relatively time intensive checks. The default implementation returns True. This method is called only on Windows. See also can_handle().

Note that for devices based on USBMS this method by default delegates to can_handle(). So you only need to override can_handle() in your subclass of USBMS.

Parametreler:

usbdevice – A usbdevice as returned by calibre.devices.winusb.scan_usb_devices()

can_handle(device_info, debug=False)[kaynak]

Unix version of can_handle_windows().

Parametreler:

device_info – (vid, pid, bcd, imalatçı, ürün, seri numarası) demeti

open(connected_device, library_uuid)[kaynak]

Tüm aygıta özel ilklendirmeleri yap. Aygıt algılandıktan sonra ama aygıtla iletişim kuran diğer fonksiyonlardan önce çağrılır. Örneğin: Kendilerini USB depolama aygıtı olarak tanıtan aygıtlarda, bu metod aygıtı bağlamakla veya aygıt otomatik bağlandıysa, nereye bağlandığını öğrenmekle sorumlu olur. calibre.devices.usbms.device.Device.open() metodunun bu fonksiyonun USB depolama aygıtlarıyla ilgili iyi bir örnek olacak uygulaması vardır.

Bu metod kullanıcıya bir ileti göstermek için bir OpenFeedback istisnası yükseltebilir.

Parametreler:

- connected_device – The device that we are trying to open. It is a tuple of (vendor id, product id, bcd, manufacturer name, product name, device serial number). However, some devices have no serial number and on Windows only the first three fields are present, the rest are None.

- library_uuid – Mevcut calibre kitaplığının UUID’si. Bir kitaplık yoksa None olabilir (örneğin komut satırından kullanıldığında).

eject()[kaynak]

Aygıtın işletim sisteminden bağını ayır/çıkart. Bu aygıtla iletişim kurmaya çalışan GUI işlerinin olup olmadığını kontrol etmez.

NOT: Bu metod diğer aygıt metodlarıyla aynı iş ipliğinde çağrılamayabilir.

post_yank_cleanup()[kaynak]

Kullanıcı aygıtı çıkarmadan birden çektiyse çağrılır.

set_progress_reporter(report_progress)[kaynak]

Süreç bilgisini raporlaması için bir fonksiyon ayarla.

Parametreler:

report_progress – Function that is called with a % progress (number between 0 and 100) for various tasks. If it is called with -1 that means that the task does not have any progress information

get_device_information(end_session=True)[kaynak]

Aygıtı aygıt bilgisi için sorgula. Bknz L{DeviceInfoQuery}.

Dönüşler:

(device name, device version, software version on device, MIME type) The tuple can optionally have a fifth element, which is a drive information dictionary. See usbms.driver for an example.

get_driveinfo()[kaynak]

driveinfo sözlüğünü döndür. Genellikle get_device_information() içinden çağrılır, ama bu sürücü için driveinfo yüklenmesi yavaşsa, SLOW_DRIVEINFO’yu ayarlamalıdır. Bu durumda, bu metod calibre tarafından kitap listeleri yüklendikten sonra çağrılır. Bunun aygıt iş ipliğinde çağrılmadığına dikkat edin, yani sürücü books() metodundaki aygıt bilgisini zulalamalı ve bu fonksiyon zulalanmış veriyi döndürmelidir.

card_prefix(end_session=True)[kaynak]

Kartlar üstündeki yollara 2 elemanlı ön ek listesi döndür. Herhangi bir kart yoksa kart ön eki olarak None ayarlanır. ÖRN. (‘/place’, ‘/place2’) (None, ‘place2’) (‘place’, None) (None, None)

total_space(end_session=True)[kaynak]

2 öğeli bir kartlar üstündeki yollara ön ekler listesi döndür. Herhangi bir kart mevcut değilse kart ön eki olarak None ayarlanır. ÖRN. (‘/place’, ‘/place2’) (None, ‘place2’) (‘place’, None) (None, None)

1. Cihaz hafızası

2. Hafıza Kartı A

3. Hafıza Kartı B

Dönüşler:

(1, 2, 3) bayt toplam alanlı 3 elemanlı liste. Eğer herhangi bir aygıt bu konumlardan birine sahip değilse 0 döndürmelidir.

free_space(end_session=True)[kaynak]

Bağlantı noktalarında kullanılabilir olan boş alanı al:

1. Cihaz hafızası

2. Kart A

3. Kart B

Dönüşler:

(1, 2, 3) bayt boş alanlı 3 elemanlı liste. Herhangi bir aygıt bu konumlardan birine sahip değilse -1 döndürmelidir.

books(oncard=None, end_session=True)[kaynak]

Return a list of e-books on the device.

Parametreler:

oncard – If ‘carda’ or ‘cardb’ return a list of e-books on the specific storage card, otherwise return list of e-books in main memory of device. If a card is specified and no books are on the card return empty list.

Dönüşler:

Bir KitapListesi.

upload_books(files, names, on_card=None, end_session=True, metadata=None)[kaynak]

Bir kitap listesini aygıta yükle. Aygıtta bir dosya zaten varsa, değiştirilmelidir. Bu metod aygıtta yeterli yer yoksa FreeSpaceError yükseltmelidir. FreeSpaceError’un metni on_card None değilse “card” kelimesini içermelidir, aksi durumda “memory” kelimesini içermelidir.

Parametreler:

- files – Bir yol listesi

- names – Kitapların aygıta yüklendikten sonra sahip olması gereken isimlerin listesi. len(isimler) == len(dosyalar)

- metadata – None değilse, Metadata nesneleri listesidir. Ana fikir kitabı aygıtta nereye koyacağını metadata kullanarak belirlemektir. len(metadata) == len(dosyalar). Sıradan kapaktan ayrı olarak (kapağa yol), ayrıca isteğe bağlı kullanılabilecek küçük resim özniteliği olabilir. Küçük resim özniteliği (genişlik, yükseklik, jpeg olarak kapak verisi) biçimindedir.

Dönüşler:

3 elemanlı demetler listesi. Liste add_books_to_metadata() ya geçirilmek üzere düşünülmüştür.

classmethod add_books_to_metadata(locations, metadata, booklists)[kaynak]

Kitaplistelerine konumlar ekle. Bu fonksiyon aygıtla iletişime geçmemelidir.

Parametreler:

- locations – L{upload_books} çağrısı sonucu

- metadata – Metadata nesneleri listesi, upload_books() için olanın aynısı.

- booklists – (books(oncard=None)(), books(oncard='carda')(), :meth`books(oncard=’cardb’)`) çağrıları sonucunu içeren bir demet.

delete_books(paths, end_session=True)[kaynak]

Aygıtta yollardeki kitapları sil.

classmethod remove_books_from_metadata(paths, booklists)[kaynak]

Kitapları metadata listesinden çıkar. Bu fonksiyon aygıtla iletişime geçmemelidir.

Parametreler:

- paths – aygıttaki kitaplara yollar.

- booklists – (books(oncard=None)(), books(oncard='carda')(), :meth`books(oncard=’cardb’)`) çağrıları sonucunu içeren bir demet.

sync_booklists(booklists, end_session=True)[kaynak]

Aygıttaki metadata’yı güncelle.

Parametreler:

booklists – (books(oncard=None)(), books(oncard='carda')(), :meth`books(oncard=’cardb’)`) çağrıları sonucunu içeren bir demet.

get_file(path, outfile, end_session=True)[kaynak]

Aygıtta path yolundaki dosyayı oku ve çıktı dosyasına yaz.

Parametreler:

outfile – sys.stdout benzeri bir dosya nesnesi veya bir open() çağrısı sonucu.

classmethod config_widget()[kaynak]

Bir QWidget döndürmelidir. QWidget aygıt arayüzü için ayarları içerir

classmethod save_settings(settings_widget)[kaynak]

Ayarları diske kaydetmelidir. config_widget() içinde oluşturulan parçacığı alır ve tüm ayarları diske kaydeder.

classmethod settings()[kaynak]

Bir opts nesnesi döndürmelidir. opts nesnesinin aygıt için biçimlerin sıralı listesi olan en az bir format_map özniteliği olmalıdır.

set_plugboards(plugboards, pb_func)[kaynak]

sürücüye mevcut santral kümesini ve belirli bir santrali seçmek için fonksiyonu sağla. Bu metod add_books ve sync_booklists’den hemen önce çağrılır.

pb_func şu imzaya sahip bir çağrılabilirdir:

def pb_func(aygıt_ismi, biçim, santraller)

Mevcut aygıt ismini (sınıf ismi veya DEVICE_PLUGBOARD_NAME), ilgilendiğiniz biçimi (‘gerçek’ bir biçim veya ‘device_db’), ve santralleri (bu metodu aldığınız yer de olan set_plugboards ile almıştınız) verirsiniz.

Dönüşler:

None veya tekli bir santral örneği.

set_driveinfo_name(location_code, name)[kaynak]

Driveinfo dosyasındaki aygıt adını ‘isim’ olarak ayarla. Bu ayar dosya tekrar oluşturulana veya ismi tekrar değiştirilene kadar kalacaktır.

Disk olmayan aygıtlar bu metodu get_device_information() metodundan dönen konum kodlarını temel alarak uygulamalıdırlar.

prepare_addable_books(paths)[kaynak]

Bir yol listesi verildiğinde, başka bir yol listesi döndürür. Bu yollar kitapların eklenebilir sürümlerini gösterir.

Bir kitap hazırlanırken hata olursa, bir yol yerine, bu kitap için döndürülen liste içindeki konum üçlü demet olmalıdır: (asıl_yol, istisna örneği, geri izleme)

startup()[kaynak]

Called when calibre is starting the device. Do any initialization required. Note that multiple instances of the class can be instantiated, and thus __init__ can be called multiple times, but only one instance will have this method called. This method is called on the device thread, not the GUI thread.

shutdown()[kaynak]

Calibre kapanırken çağrılır, tam kapanırken ya da yeniden başlatılırken. Gerekli temizliği yapın. Bu metod GUI iş ipliğinde değil aygıt sürecinde çağrılır.

get_device_uid()[kaynak]

Hazırda bağlı olan aygıt için benzersiz bir kimlik döndürmelidir (başarılı open() çağrısından hemen sonra çağrılır). ASK_TO_ALLOW_CONNECT = True ayarladıysanız bu metodu uygulamalısınız

ignore_connected_device(uid)[kaynak]

Gelecekte uid (get_device_uid() çağrısı sonucu) ile tanımlanan aygıtı göz ardı etmeli. ASK_TO_ALLOW_CONNECT = True ayarladıysanız bu metodu uygulamalısınız. Bu fonksiyon open() metodunun hemen ardından çağrılır, yani open() bir durumu zulalarsa, sürücü bu bu durumu sıfırlamalıdır.

get_user_blacklisted_devices()[kaynak]

Kullanıcının göz ardı edilmesini istediği tüm aygıtlar için aygıt uid’inden okunabilir isme haritayı döndür.

set_user_blacklisted_devices(devices)[kaynak]

Bu sürücü tarafından göz ardı edilmesi gereken aygıt uid listesini ayarla.

specialize_global_preferences(device_prefs)[kaynak]

Aygıtınız belirli bir tercihi ezmek istiyorsa bu metodu uygulayın. Ezilebilir tercih isteyen tüm çağrı sitelerinin prefs[‘bir şeyler’] yerine device_prefs[‘bir şeyler’] kullandığından emin olmalısınız. Metodunuz device_prefs.set_overrides(pref=val, pref=val, …) çağırlmalıdır. Mevcut durumda şunun için kullanılır: metadata yönetimi (prefs[‘manage_device_metadata’])

set_library_info(library_name, library_uuid, field_metadata)[kaynak]

Mevcut calibre kitaplığıyla ilgili bilgi istiyorsanız bu metodu uygulayın. Bu metod açılışta ve bağlıyken calibre kitaplığı değiştiğinde çağrılır.

is_dynamically_controllable()[kaynak]

Eklentiler başlatılırken aygıt yöneticisi tarafından çağrılır. Bu metod karakter dizisi döndürürse, a) aygıt yöneticisinin dinamik kontrol ara yüzünü destekler, ve b) bu isim eklentiyle konuşurken kullanılır.

Bu metod GUI sürecinde çağrılabilir. Bu metodu uygulayan bir sürücü iş ipliği güvenliği sağlamalıdır.

start_plugin()[kaynak]

Bu metod eklentiyi başlatmak için çağrılır. Eklenti aygıt bağlantılarını her nasıl yapıyorsa öyle kabul etmeye başlamalıdır. Eklenti zaten bağlantı kabul ediyorsa, bir şey yapma.

Bu metod GUI sürecinde çağrılabilir. Bu metodu uygulayan bir sürücü iş ipliği güvenliği sağlamalıdır.

stop_plugin()[kaynak]

Bu metod eklentiyi durdurmak için çağrılır. Eklenti artık bağlantı kabul etmemeli, ve arkasını temizlemelidir. shutdown’u bu metodun çağırması beklenebilir. Eklenti zaten bağlantı kabul etmiyorsa, bir şey yapma.

Bu metod GUI sürecinde çağrılabilir. Bu metodu uygulayan bir sürücü iş ipliği güvenliği sağlamalıdır.

get_option(opt_string, default=None)[kaynak]

opt_string tarafından gösterilen seçeneğin değerini döndür. Bu metod eklenti başlatılmadığında çağrılabilir. Seçenek mevcut değilse None döndür.

Bu metod GUI sürecinde çağrılabilir. Bu metodu uygulayan bir sürücü iş ipliği güvenliği sağlamalıdır.

set_option(opt_string, opt_value)[kaynak]

opt_string tarafından gösterilen seçeneğin değerini ayarla. Bu metod eklenti başlatılmadan çağrılabilir.

Bu metod GUI sürecinde çağrılabilir. Bu metodu uygulayan bir sürücü iş ipliği güvenliği sağlamalıdır.

is_running()[kaynak]

Eklenti başlatıldıysa True döndür, yoksa false

Bu metod GUI sürecinde çağrılabilir. Bu metodu uygulayan bir sürücü iş ipliği güvenliği sağlamalıdır.

synchronize_with_db(db, book_id, book_metadata, first_call)[kaynak]

Called during book matching when a book on the device is matched with a book in calibre’s db. The method is responsible for synchronizing data from the device to calibre’s db (if needed).

Metod iki değerli bir demet döndürmelidir. İlk değer calibre veri tabanı değiştiyse değişen kitap kimliklerinin kümesi veya değişmediyse None’dir. İlk değer boş bir kümeyse aygıttaki kitap metadata’sı calibre’nin metadata’sıyla güncellenir ve aygıta geri verilir, ama bu kitabın GUI tazelemesi yapılmaz. Calibre verisi doğruyken ve aygıta gönderilmesi gerekiyorken faydalıdır.

The second value is itself a 2-value tuple. The first value in the tuple specifies whether a book format should be sent to the device. The intent is to permit verifying that the book on the device is the same as the book in calibre. This value must be None if no book is to be sent, otherwise return the base file name on the device (a string like foobar.epub). Be sure to include the extension in the name. The device subsystem will construct a send_books job for all books with not- None returned values. Note: other than to later retrieve the extension, the name is ignored in cases where the device uses a template to generate the file name, which most do. The second value in the returned tuple indicated whether the format is future-dated. Return True if it is, otherwise return False. calibre will display a dialog to the user listing all future dated books.

Çok önemli: bu metod GUI sürecinde çalışır. Aygıt yöneticisinin iş ipliğine dikkat ederek iş ipliği güvenli olmalıdır.

book_id: kitap için veri tabanındaki calibre kimliği. book_metadata: aygıttan gelen kitap için Metadata nesnesi. first_call: Bir eşzamanlama sırasında bu ilk çağrıysa True, aksi halde False

class calibre.devices.interface.BookList(oncard, prefix, settings)[kaynak]

Bases: list

Kitap listesi. Her bir Book nesnesinin alanları olmalıdır

1. başlık

2. yazarlar

3. boyut (kitabın dosya boyutu)

4. datetime (UTC tarih demeti)

5. yol (aygıtta kitaba olan yol)

6. küçük resim (None olabilir) küçük resim ya resim verisini tutan bir karakter dizisi/bayt nesnesi olmalı ya da resme mutlak yolu (platforma göre) içeren bir image_path özniteliği olmalıdır

7. etiketler (bir karakter dizisi listesi, boş olabilir).

supports_collections()[kaynak]

Aygıt bu kitap listesi için koleksiyonları destekliyorsa True döndür.

add_book(book, replace_metadata)[kaynak]

Kitabı kitap listesine ekle. Amaç tüm aygıt-dahili metadatayı yönetmektir. Kitaplisteleri eşzamanlanmalıysa True döndür

remove_book(book)[kaynak]

Bir kitabı kitap listesinden çıkar. Aynı zamanda tüm aygıt metadata’sını düzelt

get_collections(collection_attributes)[kaynak]

collection_attributes’dan oluşturulan koleksiyon sözlüğü döndür. Sözlükteki her girdi koleksiyon_ismi:[kitap listesi] biçimindedir

Kitap listesi, serilerden oluşturulan koleksiyonlar dışında - bu durumda series_index kullanılır - kitap başlığına göre sıralanır.

Parametreler:

collection_attributes – Kitap nesnesinin öznitelik listesi

USB Depolama temelli aygıtlar

Bu tarz aygıtlar için temel sınıf :class:`calibre.devices.usbms.driver.USBMS`dir. Bu sınıf bazı işlevlerini aşağıda belgelendiği gibi temel aldıkları sınıflardan alırlar. Genel basit bir USBMS tabanlı sürücü şuna benzer:

from calibre.devices.usbms.driver import USBMS

class PDNOVEL(USBMS):
name = 'Pandigital Novel device interface'
gui_name = 'PD Novel'
description = _('Communicate with the Pandigital Novel')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'linux', 'osx']
FORMATS = ['epub', 'pdf']

VENDOR_ID = [0x18d1]
PRODUCT_ID = [0xb004]
BCD = [0x224]

THUMBNAIL_HEIGHT = 144

EBOOK_DIR_MAIN = 'eBooks'
SUPPORTS_SUB_DIRS = False

def upload_cover(self, path, filename, metadata):
coverdata = getattr(metadata, 'thumbnail', None)
if coverdata and coverdata[2]:
with open('%s.jpg' % os.path.join(path, filename), 'wb') as coverfile:
coverfile.write(coverdata[2])

class calibre.devices.usbms.device.Device(plugin_path)[kaynak]

Bases: DeviceConfig, DevicePlugin

Bu sınıf kendini USB Depolama aygıtı olarak aktaran aygıtların tüm sürücüleri için genel mantığı sağlar. Tüm platformlarda geçerli USBMS bağlama/çıkarma uygulamalarını sağlar.

VENDOR_ID = 0

VENDOR_ID bir tam sayı, tamsayı listesi veya sözlük olabilir. Bir sözlükse, sözlüklerin sözlüğü olmalıdır, şu şekilde:

{
integer_vendor_id : { product_id : [list of BCDs], ... },
...
}

PRODUCT_ID = 0

Bir tam sayı veya tam sayı listesi

BCD = None

BCD BCD tabanlı aygıtlar arasında ayrı kalmaması için None olabilir, veya bu sürücü tarafından desteklenen tüm aygıtların BCD numaralarından oluşan liste olabilir.

WINDOWS_MAIN_MEM = None

String identifying the main memory of the device in the Windows PnP id strings This can be None, string, list of strings or compiled regex

WINDOWS_CARD_A_MEM = None

String identifying the first card of the device in the Windows PnP id strings This can be None, string, list of strings or compiled regex

WINDOWS_CARD_B_MEM = None

String identifying the second card of the device in the Windows PnP id strings This can be None, string, list of strings or compiled regex

OSX_MAIN_MEM_VOL_PAT = None

Used by the new driver detection to disambiguate main memory from storage cards. Should be a regular expression that matches the main memory mount point assigned by macOS

BACKLOADING_ERROR_MESSAGE = None

MAX_PATH_LEN = 250

Aygıtta oluşturulan azami yol uzunluğu

NEWS_IN_FOLDER = True

Haberleri kendi dizinine koy

reset(key='-1', log_packets=False, report_progress=None, detected_device=None)[kaynak]

Parametreler:

- key – Aygıtın kilidini açacak tuş

- log_packets – True ise aygıta giden/aygıttan gelen paket akışı kaydedilir

- report_progress – Function that is called with a % progress (number between 0 and 100) for various tasks. If it is called with -1 that means that the task does not have any progress information

- detected_device – Aygıt tarayıcıdan gelen aygıt bilgisi

set_progress_reporter(report_progress)[kaynak]

Süreç bilgisini raporlaması için bir fonksiyon ayarla.

Parametreler:

report_progress – Function that is called with a % progress (number between 0 and 100) for various tasks. If it is called with -1 that means that the task does not have any progress information

card_prefix(end_session=True)[kaynak]

Kartlar üstündeki yollara 2 elemanlı ön ek listesi döndür. Herhangi bir kart yoksa kart ön eki olarak None ayarlanır. ÖRN. (‘/place’, ‘/place2’) (None, ‘place2’) (‘place’, None) (None, None)

total_space(end_session=True)[kaynak]

2 öğeli bir kartlar üstündeki yollara ön ekler listesi döndür. Herhangi bir kart mevcut değilse kart ön eki olarak None ayarlanır. ÖRN. (‘/place’, ‘/place2’) (None, ‘place2’) (‘place’, None) (None, None)

1. Cihaz hafızası

2. Hafıza Kartı A

3. Hafıza Kartı B

Dönüşler:

(1, 2, 3) bayt toplam alanlı 3 elemanlı liste. Eğer herhangi bir aygıt bu konumlardan birine sahip değilse 0 döndürmelidir.

free_space(end_session=True)[kaynak]

Bağlantı noktalarında kullanılabilir olan boş alanı al:

1. Cihaz hafızası

2. Kart A

3. Kart B

Dönüşler:

(1, 2, 3) bayt boş alanlı 3 elemanlı liste. Herhangi bir aygıt bu konumlardan birine sahip değilse -1 döndürmelidir.

windows_sort_drives(drives)[kaynak]

Called to disambiguate main memory and storage card for devices that do not distinguish between them on the basis of WINDOWS_CARD_NAME. For example: The EB600

can_handle_windows(usbdevice, debug=False)[kaynak]

Optional method to perform further checks on a device to see if this driver is capable of handling it. If it is not it should return False. This method is only called after the vendor, product ids and the bcd have matched, so it can do some relatively time intensive checks. The default implementation returns True. This method is called only on Windows. See also can_handle().

Note that for devices based on USBMS this method by default delegates to can_handle(). So you only need to override can_handle() in your subclass of USBMS.

Parametreler:

usbdevice – A usbdevice as returned by calibre.devices.winusb.scan_usb_devices()

open(connected_device, library_uuid)[kaynak]

Tüm aygıta özel ilklendirmeleri yap. Aygıt algılandıktan sonra ama aygıtla iletişim kuran diğer fonksiyonlardan önce çağrılır. Örneğin: Kendilerini USB depolama aygıtı olarak tanıtan aygıtlarda, bu metod aygıtı bağlamakla veya aygıt otomatik bağlandıysa, nereye bağlandığını öğrenmekle sorumlu olur. calibre.devices.usbms.device.Device.open() metodunun bu fonksiyonun USB depolama aygıtlarıyla ilgili iyi bir örnek olacak uygulaması vardır.

Bu metod kullanıcıya bir ileti göstermek için bir OpenFeedback istisnası yükseltebilir.

Parametreler:

- connected_device – The device that we are trying to open. It is a tuple of (vendor id, product id, bcd, manufacturer name, product name, device serial number). However, some devices have no serial number and on Windows only the first three fields are present, the rest are None.

- library_uuid – Mevcut calibre kitaplığının UUID’si. Bir kitaplık yoksa None olabilir (örneğin komut satırından kullanıldığında).

eject()[kaynak]

Aygıtın işletim sisteminden bağını ayır/çıkart. Bu aygıtla iletişim kurmaya çalışan GUI işlerinin olup olmadığını kontrol etmez.

NOT: Bu metod diğer aygıt metodlarıyla aynı iş ipliğinde çağrılamayabilir.

post_yank_cleanup()[kaynak]

Kullanıcı aygıtı çıkarmadan birden çektiyse çağrılır.

sanitize_callback(path)[kaynak]

Bağımsız aygıt sürücülerin create_upload_path() tarafından kullanılan yol düzenlemesini ezmesine izin veren geri çağrı.

filename_callback(default, mi)[kaynak]

Sürücülerin create_upload_path() tarafından ayarlanan varsayılan dosya ismini değiştirmesine izin veren geri çağrı.

sanitize_path_components(components)[kaynak]

Aygıta yüklenecek dosyalar için yol bileşenlerinde aygıta özel tüm düzenlemeleri yap

get_annotations(path_map)[kaynak]

path_map’i aygıtta bulunan dosyaların annotation_map’ine çöz

add_annotation_to_library(db, db_id, annotation)[kaynak]

Calibre kitaplığına bir dipnot ekle

class calibre.devices.usbms.cli.CLI[kaynak]

class calibre.devices.usbms.driver.USBMS(plugin_path)[kaynak]

Bases: CLI, Device

Tüm USBMS aygıtlar için temel sınıf. Gönderme/alma/metadata güncelleme/metadata zulalama/vs. için mantığı uygular

description = 'Bir eKitap okuyucusu ile bağlantı kur.'

Bu eklentinin ne yaptığını anlatan kısa karakter dizisi

author = 'John Schember'

Eklentinin yazarı

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

booklist_class

alias of BookList

book_class

alias of Book

FORMATS = []

Desteklenen biçimlerin sıralı listesi

CAN_SET_METADATA = []

Kitaplardaki metadata’nın GUI ile ayarlanıp ayarlanamayacağı.

get_device_information(end_session=True)[kaynak]

Aygıtı aygıt bilgisi için sorgula. Bknz L{DeviceInfoQuery}.

Dönüşler:

(device name, device version, software version on device, MIME type) The tuple can optionally have a fifth element, which is a drive information dictionary. See usbms.driver for an example.

set_driveinfo_name(location_code, name)[kaynak]

Driveinfo dosyasındaki aygıt adını ‘isim’ olarak ayarla. Bu ayar dosya tekrar oluşturulana veya ismi tekrar değiştirilene kadar kalacaktır.

Disk olmayan aygıtlar bu metodu get_device_information() metodundan dönen konum kodlarını temel alarak uygulamalıdırlar.

books(oncard=None, end_session=True)[kaynak]

Return a list of e-books on the device.

Parametreler:

oncard – If ‘carda’ or ‘cardb’ return a list of e-books on the specific storage card, otherwise return list of e-books in main memory of device. If a card is specified and no books are on the card return empty list.

Dönüşler:

Bir KitapListesi.

upload_books(files, names, on_card=None, end_session=True, metadata=None)[kaynak]

Bir kitap listesini aygıta yükle. Aygıtta bir dosya zaten varsa, değiştirilmelidir. Bu metod aygıtta yeterli yer yoksa FreeSpaceError yükseltmelidir. FreeSpaceError’un metni on_card None değilse “card” kelimesini içermelidir, aksi durumda “memory” kelimesini içermelidir.

Parametreler:

- files – Bir yol listesi

- names – Kitapların aygıta yüklendikten sonra sahip olması gereken isimlerin listesi. len(isimler) == len(dosyalar)

- metadata – None değilse, Metadata nesneleri listesidir. Ana fikir kitabı aygıtta nereye koyacağını metadata kullanarak belirlemektir. len(metadata) == len(dosyalar). Sıradan kapaktan ayrı olarak (kapağa yol), ayrıca isteğe bağlı kullanılabilecek küçük resim özniteliği olabilir. Küçük resim özniteliği (genişlik, yükseklik, jpeg olarak kapak verisi) biçimindedir.

Dönüşler:

3 elemanlı demetler listesi. Liste add_books_to_metadata() ya geçirilmek üzere düşünülmüştür.

upload_cover(path, filename, metadata, filepath)[kaynak]

Kitap kapağını aygıta yükle. Varsayılan uygulama hiçbir şey yapmaz.

Parametreler:

- path – İlgili kitabın bulunduğu klasörün tam yolu.

- filename – Kitap dosyasının uzantısız ismi.

- metadata – kitaba ait metadata. Kapak için metadata.thumbnail kullanın

- filepath – E-kitap dosyasının tam yolu

add_books_to_metadata(locations, metadata, booklists)[kaynak]

Kitaplistelerine konumlar ekle. Bu fonksiyon aygıtla iletişime geçmemelidir.

Parametreler:

- locations – L{upload_books} çağrısı sonucu

- metadata – Metadata nesneleri listesi, upload_books() için olanın aynısı.

- booklists – (books(oncard=None)(), books(oncard='carda')(), :meth`books(oncard=’cardb’)`) çağrıları sonucunu içeren bir demet.

delete_books(paths, end_session=True)[kaynak]

Aygıtta yollardeki kitapları sil.

remove_books_from_metadata(paths, booklists)[kaynak]

Kitapları metadata listesinden çıkar. Bu fonksiyon aygıtla iletişime geçmemelidir.

Parametreler:

- paths – aygıttaki kitaplara yollar.

- booklists – (books(oncard=None)(), books(oncard='carda')(), :meth`books(oncard=’cardb’)`) çağrıları sonucunu içeren bir demet.

sync_booklists(booklists, end_session=True)[kaynak]

Aygıttaki metadata’yı güncelle.

Parametreler:

booklists – (books(oncard=None)(), books(oncard='carda')(), :meth`books(oncard=’cardb’)`) çağrıları sonucunu içeren bir demet.

classmethod normalize_path(path)[kaynak]

Yolu platforma özel yol ayraçlarıyla döndür

Kullanıcı arayüzü eylemleri

If you are adding your own plugin in a ZIP file, you should subclass both InterfaceActionBase and InterfaceAction. The load_actual_plugin() method of your InterfaceActionBase subclass must return an instantiated object of your InterfaceBase subclass.

class calibre.gui2.actions.InterfaceAction(parent, site_customization)[kaynak]

Bases: QObject

Grafik kullanıcı arayüzüne alınabilecek bir “eylemi” temsil eden bir eklenti. Araç çubuğundaki tüm öğeler ve içerik menüsü bu eklentilerce uygulanır.

Bu sınıfın bu eklentiler için taban sınıf olduğunu unutmayın, ancak, eklentiyi calibre’nin eklenti sistemiyle birleştirmek için, asıl eklentiye referans eden bir kapsayıcı sınıf yapmalısınız. Örnekler için calibre.customize.builtins modülüne göz atın.

İki InterfaceAction nesnesi aynı isme sahipse, yüksek öncelikli olan önce gelir.

Sub-classes should implement the genesis(), library_changed(), location_selected(), shutting_down(), initialization_complete() and tag_browser_context_action() methods.

İlklendirildikten sonra, bu eklentinin gui üyesi ile ana calibre GUI’sine erişimi vardır. Diğer eklentilere isimle erişebilirsiniz, örneğin:

self.gui.iactions['Save To Disk']

Asıl eklentiye erişmek için, interface_action_base_plugin özniteliğini kullanın, bu öznitelik yalnızca eklenti kendisi ilklendirildiğinde kullanılabilir olur. do_user_config() gibi eklenti sınıfından metodlar kullanmak istediğinizde faydalıdır.

action_spec tarafından belirtilen QAction self.qaction olarak otomatik olarak oluşturulur ve kullanılabilir.

name = 'Implement me'

Eklenti adı. Aynı isme sahip iki eklenti varsa, yüksek öncelikli olan seçilir.

priority = 1

Eklenti önceliği. Aynı isme sahip iki eklenti varsa, yüksek öncelikli olan seçilir.

popup_type = 1

Bu eklentinin araç çubuğuna eklenmesi durumu için menü açılır pencere türü

auto_repeat = False

Bu eylemin kısayol tuşu basılı tutulduğunda otomatik olarak yinelenip yinelenmeyeceği.

action_spec = ('text', 'icon', None, None)

Of the form: (text, icon_path, tooltip, keyboard shortcut). icon, tooltip and keyboard shortcut can be None. keyboard shortcut must be either a string, None or tuple of shortcuts. If None, a keyboard shortcut corresponding to the action is not registered. If you pass an empty tuple, then the shortcut is registered with no default key binding.

action_shortcut_name = None

If not None, used for the name displayed to the user when customizing the keyboard shortcuts for the above action spec instead of action_spec[0]

action_add_menu = False

True ise, bir menü self.qaction’a otomatik olarak oluşturulur ve eklenir

action_menu_clone_qaction = False

True ise, self.qaction’un bir kopyası self.qaction’un menüsüne eklenir. Bu eylemin metninin self.qaction’dakinden farklı olmasını isterseniz, bu değişkeni yeni metne ayarlayın

dont_add_to = frozenset({})

Bu eylemin eklenmemesi gereken konumlar kümesi. Muhtemel konumlar listesi için :attr:`all_locations`a göz atın

dont_remove_from = frozenset({})

Bu eylemin çıkarılmaması gereken konumlar kümesi. Muhtemel konumlar listesi için :attr:`all_locations`a göz atın

action_type = 'global'

‘current’ türünden bir eylem mevcut görünüm üzerinde hareket, ‘global’ ise mevcut görnüm değil bir bütün olarak calibre üzerinde hareket anlamına gelir

accepts_drops = False

True ise, bu interfaceAction sürükle bırak eylemleriyle etkileşme şansına sahip olacaktır. Detaylar için accept_enter_event(), :meth`:accept_drag_move_event`, drop_event() metodlarına göz atın.

accept_enter_event(event, mime_data)[kaynak]

Bu metod bu arayüz eylemi sürükleme olayını yapabilme becerisine sahipse True döndürmeli. Olayda kabul et/göz ardı et çağırmayın, bu calibre UI’si tarafından halledilir.

accept_drag_move_event(event, mime_data)[kaynak]

Bu metod bu arayüz eylemi sürükleme olayını yapabilme becerisine sahipse True döndürmeli. Olayda kabul et/göz ardı et çağırmayın, bu calibre UI’si tarafından halledilir.

drop_event(event, mime_data)[kaynak]

Bu metod bazı kullanışlı eylemler gerçekleştirmeli ve bu arayüz bırak eylemini yapabilme becerisine sahipse True döndürmeli. Olayda kabul et/göz ardı et çağırmayın, bu calibre UI’si tarafından halledilir. Bu fonksiyonda engelleyici/uzun işlemler yapmayın. Bunun yerine bir sinyal yayın veeya bir QTimer.singleShot kullanın ve çabucak dönün. Örnekler için dahili eylemlere göz atabilirsiniz.

create_menu_action(menu, unique_name, text, icon=None, shortcut=None, description=None, triggered=None, shortcut_name=None, persist_shortcut=False)[kaynak]

Convenience method to easily add actions to a QMenu. Returns the created QAction. This action has one extra attribute calibre_shortcut_unique_name which if not None refers to the unique name under which this action is registered with the keyboard manager.

Parametreler:

- menu – Yeni oluşturulan eylemin ekleneceği QMenu

- unique_name – A unique name for this action, this must be globally unique, so make it as descriptive as possible. If in doubt, add an UUID to it.

- text – Eylem metni.

- icon – Either a QIcon or a file name. The file name is passed to the QIcon.ic() builtin, so you do not need to pass the full path to the images folder.

- shortcut – Bir karakter dizisi, karakter dizisi listesi, None veya False. False ise, bu eylem için klavye kısayolu kaydedilmez. None ise, varsayılan bağlayıcısı olmayan bir klavye kısayolu kaydedilir. Karakter dizisi ve karakter dizisi listeleri belirtilen varsayılan tuş atamasını kaydeder.

- description – Bu eylem için tanım. Araç ipuçlarını ayarlamak için kullanılır.

- triggered – Oluşturulan eylemin tetiklenen sinyaline bağlanmak üzere bir çağrılabilir.

- shortcut_name – The text displayed to the user when customizing the keyboard shortcuts for this action. By default it is set to the value of text.

- persist_shortcut – Shortcuts for actions that don’t always appear, or are library dependent, may disappear when other keyboard shortcuts are edited unless `persist_shortcut` is set True.

load_resources(names)[kaynak]

Bu eklenti bir ZIP dosyasıyla gelirse (kullanıcı tarafından eklenmiş eklenti), bu metod ZIP dosyasından kaynakları yüklemenizi sağlar.

Örneğin bir resmi yüklemek için:

pixmap = QPixmap()
pixmap.loadFromData(tuple(self.load_resources(['images/icon.png']).values())[0])
icon = QIcon(pixmap)

Parametreler:

names – Ayırıcı olarak / kullanılarak ZIP dosyasındaki kaynaklara giden yolların listesi

Dönüşler:

A dictionary of the form {name : file_contents}. Any names that were not found in the ZIP file will not be present in the dictionary.

genesis()[kaynak]

Bu eklentiyi kur. Yalnızca ilklendirme aşamasında bir kere çağrılır. self.gui kullanılabilirdir. action_spec tarafından belirtilen eylem self.qaction ile kullanılabilir.

location_selected(loc)[kaynak]

Calibre’de gösterilen kitap listesi her değiştiğinde çağrılır. Şu an loc için değerler: kitaplık, ana, kart ve kartb.

Bu metod bu eylemi ve alt eylemlerini konuma uygun olarak açmalı/kapamalı.

library_about_to_change(olddb, db)[kaynak]

Mevcut kitaplık değiştiğinde çağrılır.

Parametreler:

- olddb – The LibraryDatabase corresponding to the previous library.

- db – The LibraryDatabase corresponding to the new library.

library_changed(db)[kaynak]

Mevcut kitaplık değiştiğinde çağrılır.

Parametreler:

db – Mevcut kitaplığa karşılık gelen LibraryDatabase.

gui_layout_complete()[kaynak]

Ana GUI’nin yerleşimi tamamlandığında her eylem için bir kere çağrılır. Eyleminiz yerleşimde değişiklik yapmalıysa, bu değişiklikler initialization_complete() yerine burada olmalıdır.

initialization_complete()[kaynak]

Ana GUI ilklendirmesi bittiğinde her eylem için bir kere çağrılır.

tag_browser_context_action(index)[kaynak]

Called when displaying the context menu in the Tag browser. index is the QModelIndex that points to the Tag browser item that was right clicked. Test it for validity with index.valid() and get the underlying TagTreeItem object with index.data(Qt.ItemDataRole.UserRole). Any action objects yielded by this method will be added to the context menu.

shutting_down()[kaynak]

Ana GUI kapanma evresindeyken her eklenti için bir kere çağrılır. Kullanılan kaynakları bırakın, ama kapatmayı uzun süre engellememeye çalışın.

class calibre.customize.InterfaceActionBase(*args, **kwargs)[kaynak]

Temeller: Plugin

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

author = 'Kovid Goyal'

Eklentinin yazarı

type = 'Kullanıcı arayüzü eylemi'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

can_be_disabled = False

False ise, kullanıcının bu eklentiyi kapatma şansı olmayacaktır. Dikkatli kullanın.

load_actual_plugin(gui)[kaynak]

Bu metod asıl arayüz eylem eklenti nesnesini döndürmeli.

Preferences plugins
class calibre.customize.PreferencesPlugin(plugin_path)[kaynak]

Temeller: Plugin

Tercihler iletişim penceresinde gösterilen bir parçacığı temsil eden eklenti.

Bu eklentinin sadece bir önemli metodu var create_widget(). Eklentinin çeşitli alanları UI’de nasıl kategorilendiğini kontrol eder.

supported_platforms = ['windows', 'osx', 'linux']

Bu eklentinin çalıştığı platformların listesi. Örneğin: ['windows', 'osx', 'linux']

author = 'Kovid Goyal'

Eklentinin yazarı

type = 'Tercihler'

Eklenti türü. Eklentileri GUI’de kategorize etmek için kullanılır

can_be_disabled = False

False ise, kullanıcının bu eklentiyi kapatma şansı olmayacaktır. Dikkatli kullanın.

config_widget = None

ConfigWidget adında ConfigWidgetInterface’yi uygulayan sınıfı içeren modüle yolu içe aktar. create_widget() tarafından kullanılır.

category_order = 100

Bu eklentinin :attr:`category`si kategori listesinden nerede bulunmalı.

name_order = 100

Bu eklentinin :attr:`gui_name`si bir kategorideki isimler listesinde nerede bulunmalı

category = None

Bu eklentinin bulunması gereken kategori

gui_category = None

Bu eklenti için kullanıcıya gösterilecek kategori ismi

gui_name = None

Bu eklenti için kullanıcıya gösterilecek isim

icon = None

Eklenti simgesi, mutlak yol olmalıdır

description = None

Araç ipuçları ve benzerleri için kullanılacak tanımlama

create_widget(parent=None)[kaynak]

Bu tercih grubunu ayarlamak için kullanılan asıl Qt parçacığını oluştur ve döndür. Parçacık :class:`calibre.gui2.preferences.ConfigWidgetInterface`yi uygulamalıdır.

Varsayılan uygulama parçacığı ilklendirmek için config_widget kullanır.

class calibre.gui2.preferences.ConfigWidgetInterface[kaynak]

Bu sınıf Tercihler iletişim penceresinde gösterilen tüm parçacıkların uygulaması gereken arayüzü tanımlar. Bu arayüzü uygulayan ve çeşitli kolaylık sağlayıcı metod tanımlayan ConfigWidgetBase taban sınıfına göz atın.

changed_signal = None

Bu sinyal kullanıcı bu parçacıkta her değişiklik yaptığında yayılmalıdır

supports_restoring_to_defaults = True

restore_to_defaults() metodu uygulanmışsa True olarak ayarla.

restore_defaults_desc = "Varsayılan ayarları yükle. Bu işlemi tamamlamak için Tamam'a tıklamalısınız."

“Varsayılanları geri yükle” düğmesine ilişkin ipucu

restart_critical = False

True ise Tercihler iletişim penceresi kullanıcının daha fazla tercih ayarlamasına izin vermez. Yalnızca commit() True döndürdüğünde etkilidir.

genesis(gui)[kaynak]

Parçacık görüntülenmeden önce bir kere çağrılır, gerekli tüm kurulumu gerçekleştirmelidir.

Parametreler:

gui – Ana calibre grafik kullanıcı arayüzü

initialize()[kaynak]

Tüm yapılandırma değerlerini başlangıç değerlerine (yapılandırma dosyalarında saklanan değerler) ayarlamanız gerekir. Bir “return” ifadesi isteğe bağlıdır. İletişim kutusunun gösterilmemesi durumunda False değerini döndürün.

restore_defaults()[kaynak]

Tüm yapılandırma değerlerini varsayılan değerlerine ayarlamalıdır.

commit()[kaynak]

Değişen ayarları kaydet. Değişiklikler yeniden başlatma gerektiriyorsa True döndür, aksi halde False. Bir hata olduğunu belirtmek için bir AbortCommit istisnası yükselt. Hatanın ne olduğu ve nasıl düzeltileceğiyle ilgili kullanıcıya bilgi vermek sizin sorumluluğunuzdadır.

refresh_gui(gui)[kaynak]

Bu parçacık işlendikten sonra bir kere çağrılır. Gui’nin değiştirilen ayarları tekrar okumasına yol açmakla sorumludur. Varsayılan olarak GUI çeşitli öğeleri zaten tekrar ilklendirir, yani çoğu parçacığın bu metodu kullanmasına gerek yoktur.

initial_tab_changed()[kaynak]

Başlangıçta görüntülenen sekme, widget gösterilmeden önce ancak başlatıldıktan sonra değiştirilirse çağrılır.

class calibre.gui2.preferences.ConfigWidgetBase(parent=None)[kaynak]

Kolayca standart kontrol kutuları, açılır kapanır kutular, metin alanları benzeri parçacıklar eklemek için kod içeren temel sınıf. register() metoduna göz atın.

Bu sınıf kaydedilen ayarlar için bildirim değişikliği, varsayılana geri dönme, gui nesneleri ve ayar nesneleri arası geçişleri vs. otomatik yönetir.

Eğer yapılandırma parçacığınız bu sınıftan türemiş fakat kaydedilmemiş ayarlar içeriyorsa, ConfigWidgetInterface metodlarını ezmeli ve ezilen metodlar içinden taban sınıfın metodlarını çağırmalısınız.

changed_signal

Bu sinyal kullanıcı bu parçacıkta her değişiklik yaptığında yayılmalıdır

supports_restoring_to_defaults = True

restore_to_defaults() metodu uygulanmışsa True olarak ayarla.

restart_critical = False

True ise Tercihler iletişim penceresi kullanıcının daha fazla tercih ayarlamasına izin vermez. Yalnızca commit() True döndürdüğünde etkilidir.

register(name, config_obj, gui_name=None, choices=None, restart_required=False, empty_string_is_None=True, setting=<class 'calibre.gui2.preferences.Setting'>)[kaynak]

Bir ayar kaydet.

Parametreler:

- name – Ayar adı

- config_obj – Ayarı okuyan/yazan yapılandırma nesnesi

- gui_name – Ayarın değiştirilebilmesi için bir arayüz sunan GUI nesnesinin adı. Varsayılan olarak 'opt_' + isim beklenir.

- choices – Bu ayar çoklu seçim (açılır kutu) temelli bir ayarsa, seçimler listesi. Liste [(gui ismi, değer), ...] şeklinde ikili demetler listesidir

- setting – Bu ayarı yönetmekle sorumlu sınıf. Varsayılan sınıf nerdeyse tüm durumları ele alır, yani bu parametre nadiren kullanılır.

initialize()[kaynak]

Tüm yapılandırma değerlerini başlangıç değerlerine (yapılandırma dosyalarında saklanan değerler) ayarlamanız gerekir. Bir “return” ifadesi isteğe bağlıdır. İletişim kutusunun gösterilmemesi durumunda False değerini döndürün.

commit(*args)[kaynak]

Değişen ayarları kaydet. Değişiklikler yeniden başlatma gerektiriyorsa True döndür, aksi halde False. Bir hata olduğunu belirtmek için bir AbortCommit istisnası yükselt. Hatanın ne olduğu ve nasıl düzeltileceğiyle ilgili kullanıcıya bilgi vermek sizin sorumluluğunuzdadır.

restore_defaults(*args)[kaynak]

Tüm yapılandırma değerlerini varsayılan değerlerine ayarlamalıdır.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü

Komut Satırı Arayüzü

Not

On macOS, the command line tools are inside the calibre bundle, for example, if you installed calibre in /Applications the command line tools are in /Applications/calibre.app/Contents/MacOS/. So, for example, to run ebook-convert you would use: /Applications/calibre.app/Contents/MacOS/ebook-convert.

Documented commands

- calibre
- calibre-customize
- calibre-debug
- calibre-server
- calibre-smtp
- calibredb
- ebook-convert
- ebook-edit
- ebook-meta
- ebook-polish
- ebook-viewer
- fetch-ebook-metadata
- lrf2lrs
- lrfviewer
- lrs2lrf
- web2disk

Undocumented commands

- ebook-device

- markdown-calibre

Belgelendirilmemiş komutların nasıl kullanılacağını terminalden bir parametre vermeden kullanarak görebilirsiniz.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- calibre

calibre

calibre [options] [path_to_ebook or calibre url ...]

Launch the main calibre Graphical User Interface and optionally add the e-book at path_to_ebook to the database. You can also specify calibre URLs to perform various different actions, than just adding books. For example:

calibre://view-book/test_library/1842/epub

Will open the book with id 1842 in the EPUB format from the library “test_library” in the calibre E-book viewer. Library names are the folder names of the libraries with spaces replaced by underscores. A full description of the various URL based actions is in the User Manual.

Whenever you pass arguments to calibre that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--detach

Detach from the controlling terminal, if any (Linux only)

--help, -h

bu yardım iletisini göster ve çık

--ignore-plugins

Özel eklentileri yoksay, Calibre'nin başlamasına engel olan eklentiler kurmuşsanız faydalı olacaktır

--no-update-check

Güncellemeleri denetleme

--shutdown-running-calibre, -s

Çalışmakta olan bir Calibre oturumu varsa kapanmasını sağlar. Devam etmekte olan işlemler olması halinde uyarı vermeksizin kapatılacağı için dikkatle kullanınız.

--start-in-tray

Sistem çekmecesinde küçülterek başlat.

--verbose, -v

Yoksayın, kullanmayın. Sadece eski nedenlerle mevcut

--version

programın sürüm numarasını göster ve çık

--with-library

Belirtilen konumdaki kitaplığı kullan.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- calibre-customize

calibre-customize

calibre-customize seçenekleri

Harici eklentiler yükleyerek Calibre’ye özelleştir.

Whenever you pass arguments to calibre-customize that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--add-plugin, -a

Eklentiyi içeren ZIP dosyasının bulunduğu konumu belirterek eklenti ekleyin.

--build-plugin, -b

Eklenti geliştiricileri için: Eklentiyi geliştirdiğiniz klasörün yolu. Bu komut, eklentiyi otomatik olarak sıkıştıracak ve calibre'de güncelleyecektir.

--customize-plugin

Customize plugin. Specify name of plugin and customization string separated by a comma. The customization string is the same as you would enter when customizing the plugin in the main calibre GUI.

--disable-plugin

İsimlendirilmiş eklentiler geçersiz

--enable-plugin

İsimlendirilmiş eklentiler geçerli

--help, -h

bu yardım iletisini göster ve çık

--list-plugins, -l

Kurulu eklentileri listele

--remove-plugin, -r

İsminden yararlanarak özel bir eklentiyi çıkart. Programla gelen eklentiler üzerinde etkisi yoktur

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- calibre-debug

calibre-debug

calibre-debug [seçenekler]

Calibre’de hata ayıklamak için kullanışlı çeşitli komut satırı arayüzleri. Parametreler olmadan bu komut, yerleşik bir Python yorumlayıcısını başlatır. Ayrıca anacalibre programını, calibre E-kitap görüntüleyiciyi ve calibre düzenleyiciyi hata ayıklama modunda çalıştırabilirsiniz.

Bunun yanısıra, yazı tipi alt ayarı, e-kitap fark aracı gibi özel komut satırı araçlarına sahip olmayan çeşitli calibre parçaları için arabirimler içerir.

Bağımsız komut dosyaları çalıştırmak için de calibre-debug’u kullanabilirsiniz. Bunu yapmak için şu şekilde kullanın:

calibre-debug -e myscript.py -- --option1 --option2 file1 file2 …–‘den sonraki her şey betiğe aktarılır. Ayrıca calibre-debug komutunu betiklerde şu şekilde kullanabilirsiniz:

#!/usr/bin/env -S calibre-debug -e -- –

Whenever you pass arguments to calibre-debug that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--add-simple-plugin

Basit bir eklenti ekleyin (mesela sadece bir .py dosyası olan). Eklenti kodunu içeren py dosyasının konumunu belirtin.

--command, -c

Python kodunu çalıştır.

--debug-device-driver, -d

Cihaz tarama hata ayıklama çıktısı

--default-programs

Calibre'yi Varsayılan Windows Programlarına ekle(çıkart) --default-programs = (register|unregister)

--diff

Calibre fark aracını çalıştır. Mesela: Calibre-debug --diff dosya1 dosya2

--edit-book

Calibre "Kitap Düzenleme" aracını hata ayıklama kipinde başlat.

--exec-file, -e

Dosyadaki Python kodunu çalıştır.

--explode-book, -x

Kitabı patlatarak belirtilen klasöre yerleştirin. Kullanım: -x file.epub çıktı_klasörü Kitabı, standart HTML düzenleme araçlarını kullanarak düzenleyebileceğiniz bir HTML dosyaları ve üstveri koleksiyonu olarak dışa aktarır. EPUB, AZW3, HTMLZ ve DOCX dosyalarıyla çalışır.

--export-all-calibre-data

Tüm calibre verilerini dışa aktarın (kitaplar/ayarlar/eklentiler). Normalde, dışa aktarma klasörü ve dışa aktarılacak kitaplıklar sorulacaktır. Soruları atlamak için bunları komut satırı argümanları olarak da belirleyebilirsiniz. Dışa aktarma klasörü ve kitaplıkları için mutlak yollar kullanın. Tüm kitaplıkları dışa aktarmak için "all" özel anahtar sözcüğü kullanılabilir. Örnekler: calibre-debug --export-all-calibre-data # interaktif kullanım için calibre-debug --export-all-calibre-data /yol/boş/çıktı/klasörü /yol/kitaplık/ klasör1 /yol/kitaplık2 calibre-debug --export-all-calibre-data /çıktı/klasör all # bilinen tüm kitaplıkları dışa aktar

--fix-multiprocessing

İç kullanım için

--gui, -g

Arayüzü hata ayıklama etkin olara çalıştır. Hata çıktısı stdout ve stderr üzerine yazılır.

--gui-debug

Arayüzü belirtilen konuma günlük kaydı yapan bir hata ayıklama konsolu ile çalıştır. Sadece iç kullanım içindir. Arayüzü hata ayıklama kipinde çalıştırma için -g seçeneğini kullanın

--help, -h

bu yardım iletisini göster ve çık

--implode-book, -i

Önceden patlamış bir kitabı içeri yerleştirin. Kullanım: -i çıktı_dizini dosya.epub Kitabı, --explode-book komutu ile önceden oluşturulmuş olması gereken çıktı_dizini içerisindeki dosyalardan içeri aktarır. Patlatırken kullanılanla aynı dosya türünü belirttiğinizden emin olun.

--import-calibre-data

Önceden dışa aktarılmış Calibre verisini içe aktar

--inspect-mobi, -m

Belritilen konumlardaki MOBI dosyalarını denetleyin

--paths

Calibre ortamını kurmak için zorunlu olan konumların çıktısını ver

--run-plugin, -r

Komut satırı arabirimi sağlayan bir eklenti çalıştırın. Örneğin: calibre-debug -r "Eklenti adı" -- dosya1 --se`çenek1 --`’``den sonraki her şey eklentiye argüman olarak iletilir.

--run-test, -t

Adlandırılmış test(ler)i çalıştırın. Tüm testleri çalıştırmak için "all" özel değerini kullanın. Test adı bir nokta ile başlıyorsa, bir modül adı olduğu varsayılır. Test adı @ işareti ile başlıyorsa, bir kategori adı olduğu varsayılır.

--run-without-debug

DEBUG bayrağı ayarlanmış şekilde çalıştırmayın

--shutdown-running-calibre, -s

Çalışmakta olan bir Calibre oturumu varsa kapanmasını sağlar. Devam etmekte olan işlemler olması halinde uyarı vermeksizin kapatılacağı için dikkatle kullanınız.

--subset-font, -f

Belirtilen yazı tipini alt kümele. Bu seçenekten sonra -- kullanarak seçeneği yazı tipi alt kümeleme programına geçirebilirsiniz.

--test-build

Derlemede ikili modülleri dene

--version

programın sürüm numarasını göster ve çık

--viewer, -w

E-kitap görüntüleyiciyi hata ayıklama kipinde çalıştır

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- calibre-server

calibre-server

calibre-server [seçenekler] [kitaplık klasörü yolu...]

Calibre İçerik sunucusunu başlatın. Calibre İçerik sunucusu, calibre kitaplıklarınızı internet üzerinden gösterir. Kitaplık klasörlerine giden yolu calibre-server argümanı olarak belirtebilirsiniz. Herhangi bir yol belirtmezseniz, ana calibre programının bildiği tüm kütüphaneler kullanılacaktır.

Whenever you pass arguments to calibre-server that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--access-log

Erişim günlüğü dosyasının yolu. Bu günlük, sunucuya bağlanan ve istekte bulunan istemciler hakkında bilgi içerir. Varsayılan olarak erişim kaydı yapılmaz.

--ajax-timeout

Sorgu yaparken sunucudan yanıt bekleme süresi (saniye cinsinden).

--auth-mode

Choose the type of authentication used. Sunucu tarafından kullanılan HTTP kimlik doğrulama modunu ayarlayın. Bu sunucuyu bir SSL proxy arkasına yerleştiriyorsanız "temel" olarak ayarlayın. Aksi takdirde, bu ayarı "otomatik" olarak bırakın ki bu şekilde SSL ayarlanmışsa "temel" kullanılacaktır, diğer türlü "digest (sindirme)" yöntemi kullanacaktır.

--auto-reload

Kaynak kod değiştiğinde sunucuyu otomatik olarak tekrar yükle. Geliştirme için faydalıdır. Kapatma sırasındaki zaman aşımı için küçük bir değer de belirlemelisiniz.

--ban-after

Number of login failures for ban. The number of login failures after which an IP address is banned

--ban-for

Ban IP addresses that have repeated login failures. Temporarily bans access for IP addresses that have repeated login failures for the specified number of minutes. Useful to prevent attempts at guessing passwords. If set to zero, no banning is done.

--book-list-mode

Choose the default book list mode. Set the default book list mode that will be used for new users. Individual users can override the default in their own settings. The default is to use a cover grid.

--compress-min-size

Minimum size for which responses use data compression (in bytes).

--custom-list-template

Path to a JSON file containing a template for the custom book list mode. The easiest way to create such a template file is to go to Preferences-> Sharing over the net-> Book list template in calibre, create the template and export it.

--daemonize

Run process in background as a daemon (Linux only).

--displayed-fields

Restrict displayed user-defined fields. /opds ve /mobile görünümlerinde İçerik sunucusu tarafından görüntülenmeyecek kullanıcı tanımlı üstveri alanlarının virgülle ayrılmış listesi. Bu seçeneği belirtirseniz, bu listede olmayan alanlar görüntülenmeyecektir. Örneğin: my_rating,my_tags

--enable-allow-socket-preallocation, --disable-allow-socket-preallocation

Socket pre-allocation, for example, with systemd socket activation. By default, this option is enabled.

--enable-auth, --disable-auth

Sunucuya erişmek için parola tabanlı kimlik doğrulama. Normalde, sunucu sınırsızdır ve herkesin erişmesine izin verir. Bu seçenek ile erişimi, önceden tanımlanmış kullanıcılara erişim verecek şekilde kısıtlayabilirsiniz. By default, this option is disabled.

--enable-fallback-to-detected-interface, --disable-fallback-to-detected-interface

Fallback to auto-detected interface. Sunucu herhangi bir nedenle listen_on seçeneğinde belirtilen arayüze bağlanamıyorsa, dış dünyaya bağlanan bir arayüzü tespit etmeye ve ona bağlanmaya çalışacaktır. By default, this option is enabled.

--enable-local-write, --disable-local-write

Allow un-authenticated local connections to make changes. Normalde, kimlik doğrulamayı açmazsanız, sunucu salt okunur modda çalışır, böylece isimsiz kullanıcıların calibre kitaplıklarınızda değişiklik yapmasına izin verilmez. Bu seçenek, sunucunun çalıştığı bilgisayardan bağlanan herkesin değişiklik yapmasına izin verir. Bu, sunucuyu kimlik doğrulama olmadan çalıştırmak, ancak yine de calibre kitaplıklarınızda değişiklik yapmak için calibredb'yi kullanmak istiyorsanız kullanışlıdır. Bu seçeneği açmanın, bilgisayarda çalışan herhangi bir programın calibre kitaplıklarınızda değişiklik yapabileceği anlamına geldiğini unutmayın. By default, this option is disabled.

--enable-log-not-found, --disable-log-not-found

Log HTTP 404 (Not Found) requests. Normalde sunucu, bulunmayan kaynaklar için tüm HTTP isteklerini günlüğe kaydeder. Sunucunuz botlar tarafından hedefleniyorsa, bu çok sayıda günlük spam'ı oluşturabilir. Kapatmak için bu seçeneği kullanın. By default, this option is enabled.

--enable-use-bonjour, --disable-use-bonjour

Advertise OPDS feeds via BonJour. OPDS beslemelerini BonJour hizmeti aracılığıyla tanıtın, böylece OPDS tabanlı okuma uygulamaları sunucuyu otomatik olarak algılayabilir ve sunucuya bağlanabilir. By default, this option is enabled.

--enable-use-sendfile, --disable-use-sendfile

Zero copy file transfers for increased performance. This will use zero-copy in-kernel transfers when sending files over the network, increasing performance. However, it can cause corrupted file transfers on some broken filesystems. If you experience corrupted file transfers, turn it off. By default, this option is enabled.

--help, -h

bu yardım iletisini göster ve çık

--ignored-fields

Ignored user-defined metadata fields. /opds ve /mobile görünümlerinde İçerik sunucusu tarafından görüntülenmeyecek kullanıcı tanımlı üstveri alanlarının virgülle ayrılmış listesi. Örneğin: my_rating,my_tags

--listen-on

The interface on which to listen for connections. The default is to listen on all available IPv6 and IPv4 interfaces. You can change this to, for example, "127.0.0.1" to only listen for IPv4 connections from the local machine, or to "0.0.0.0" to listen to all incoming IPv4 connections.

--log

Sunucu günlüğü için günlük dosyasının yolu. Bu günlük, erişim günlüklerini değil, sunucu bilgilerini ve hataları içerir. Varsayılan olarak stdout'a yazılır.

--manage-users

Bu sunucuya bağlanmasına izin verilen kullanıcıların veritabanını yönetin. Bir -- ekleyerek otomatik modda kullanabilirsiniz. Ayrıntılar için calibre-server --manage-users -- help konusuna bakın. Ayrıca bkz. --userdb seçeneği.

--max-header-line-size

Max. size of single HTTP header (in KB).

--max-job-time

Maximum time for worker processes. Maximum amount of time worker processes are allowed to run (in minutes). Set to zero for no limit.

--max-jobs

Maximum number of worker processes. Worker processes are launched as needed and used for large jobs such as preparing a book for viewing, adding books, converting, etc. Normally, the max. number of such processes is based on the number of CPU cores. You can control it by this setting.

--max-log-size

Max. log file size (in MB). Sunucu tarafından oluşturulan günlük dosyalarının maksimum boyutu. Günlük bu boyuttan daha büyük olduğunda, otomatik olarak eski kayıtların üzerine yazmaya başlar. Eski kayıtların üzerine yazılmasını devre dışı bırakmak için sıfıra ayarlayın.

--max-opds-items

Maximum number of books in OPDS feeds. Sunucunun tek bir OPDS edinme akışında döndüreceği maksimum kitap sayısı.

--max-opds-ungrouped-items

Maximum number of ungrouped items in OPDS feeds. Group items in categories such as author/tags by first letter when there are more than this number of items. Set to zero to disable.

--max-request-body-size

Sunucuya yüklenen dosyalar için izin verilen maksimum boyut (MB olarak).

--num-per-page

Number of books to show in a single page. The number of books to show in a single page in the browser.

--pidfile

Süreç kimliğini belirtilen dosyaya yaz

--port

The port on which to listen for connections.

--search-the-net-urls

Path to a JSON file containing URLs for the "Search the internet" feature. The easiest way to create such a file is to go to Preferences-> Sharing over the net->Search the internet in calibre, create the URLs and export them.

--shutdown-timeout

Total time in seconds to wait for clean shutdown.

--ssl-certfile

Path to the SSL certificate file.

--ssl-keyfile

Path to the SSL private key file.

--timeout

Time (in seconds) after which an idle connection is closed.

--trusted-ips

Allow un-authenticated connections from specific IP addresses to make changes. Normalde, kimlik doğrulamayı açmazsanız, sunucu salt okunur modda çalışır, böylece isimsiz kullanıcıların calibre kitaplıklarınızda değişiklik yapmasına izin verilmez. Bu seçenek, belirtilen IP adreslerinden bağlanan herkesin değişiklik yapmasına izin verir. Virgülle ayrılmış bir adres veya ağ özellikleri listesi olmalıdır. Bu, sunucuyu kimlik doğrulama olmadan çalıştırmak, ancak yine de calibre kitaplıklarınızda değişiklik yapmak için calibredb'yi kullanmak istiyorsanız kullanışlıdır. Bu seçeneği açmanın, belirtilen IP adreslerinden bağlanan herkesin calibre kitaplıklarınızda değişiklik yapabileceği anlamına geldiğini unutmayın.

--url-prefix

A prefix to prepend to all URLs. Bu sunucuyu bir ters proxy arkasında çalıştırmak istiyorsanız kullanışlıdır. Örneğin URL öneki olarak /calibre kullanın.

--userdb

Path to the user database to use for authentication. The database is a SQLite file. To create it use --manage-users. You can read more about managing users at: https://manual.calibre-ebook.com/tr/server.html#managing-user-accounts-from-the-command-line-only

--version

programın sürüm numarasını göster ve çık

--worker-count

Number of worker threads used to process requests.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- calibre-smtp

calibre-smtp

calibre-smtp [seçenekler] [kimden kime metin]

SMTP protokolü kullanarak e-posta gönder. calibre-smtp iki işlem kipine sahiptir. Derleme kipinde kimden kime ve metni belirtirsiniz, bunlar e-posta iletisi göndermek için inşa edilir ve kullanılırlar. Filtre kipinde, calibre-smtp e-posta iletisini STDIN’den okur ve gönderir.

metin e-posta iletisinin gövdesidir. metin belirtilmemişse, e-posta iletisi tamamen STDIN’den okunur. kimden gönderecinin, kime ise alıcının e-posta adresidir. STDIN’den tam bir e-posta okunduğunda, kimden ve kime yalnızca SMTP görüşmesinde kullanılır, ileti başlıkları değiştirilmez.

Whenever you pass arguments to calibre-smtp that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--fork, -f

Çatallan ve iletiyi arka planda ilet. Bu seçeneği kullanırsanız, iletim hatalarını ele almak için ayrıca --outbox kullanmalısınız.

--help, -h

bu yardım iletisini göster ve çık

--localhost, -l

Yerel makinenin adı. SMTP sunucusuna bağlanılırken kullanılır.

--outbox, -o

Başarısız epostaların saklanacağı maildir klasörünün yolu.

--timeout, -t

Bağlantı için zaman aşımı

--verbose, -v

Daha çok ayrıntı ver

--version

programın sürüm numarasını göster ve çık

COMPOSE MAIL

Bir eposta oluşturma seçenekleri. Eğer metin girilmediyse yoksayılır

--attachment, -a

Epostaya eklenecek dosya

--subject, -s

Epostanın konusu

SMTP RELAY

Bir SMTP sunucusunu posta göndermek için kullanma seçenekleri. Calibre –relay seçeneği kullanılmadığı durumlarda postayı doğrudan göndermeye çalışacaktır

--cafile

TLS kullanırken sunucu sertifikasını doğrulamak için kullanılan, PEM biçiminde birleştirilmiş CA sertifikaları dosyasının yolu. Varsayılan olarak, sistem CA sertifikaları kullanılır.

--dont-verify-server-certificate

TLS kullanarak bağlanırken sunucu sertifikasını doğrulama. Bu calibre'nin 3.27'den önceki sürümlerinde varsayılan davranıştı. Kendinden imzalı veya başka bir şekilde geçersiz sertifikaya sahip bir geçiş kullanıyorsanız, 3.27 öncesi davranışı geri yüklemek için bu seçeneği kullanabilirsiniz.

--encryption-method, -e

Bağlantıda kullanılan şifreleme yöntemi. TLS, SSL ve NONE arasından birini seçin. Varsayılan TLS'tir. NONE seçmek çok güvensizdir

--password, -p

Giriş için parola

--port

Posta sunucusuna bağlanılacak bağlantı noktası. Şifreleme yöntemi SSL ise varsayılan olarak 465, diğer durumlarda 25'tir.

--relay, -r

Eposta göndermek için kullanılacak SMTP sunucusu.

--username, -u

Giriş için kullanıcı adı

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- calibredb

calibredb

calibredb komut [seçenekler] [değişkenler]

calibredb calibre veritabanına giden komut satırı arayüzüdür. Aşağıda belgelenen birkaç alt komutu vardır.

calibredb, yola göre belirtilen calibre veritabanını

veya yerel makinede veya internet üzerinden. Bir calibre İçerik sunucusu`nu :command:`calibre-server programını kullanarak veya ana calibre programında Bağlan/paylaş  →  İçerik sunucusunu Başlat seçeneğine tıklayarak başlatabilirsiniz. calibredb calibre kitaplıklarınızda değişiklik yapabildiğinden, önce sunucuda kimlik doğrulamayı ayarlamanız gerekir. Bunu yapmanın

iki yolu vardır:

- Yalnızca aynı bilgisayarda çalışan bir sunucuya bağlanmayı planlıyorsan

calibredb dahil olmak üzere yerel bilgisayarda çalışan herhangi bir programın calibre verilerinizde değişiklik yapmasına izin vermek için içerik sunucusunun --enable-local-write'' seçeneğini kullanabilirsin. Sunucuyu ana calibre programından çalıştırırken, bu seçenek şu şekildedir:guilabel:`Tercihler->Ağ üzerinden paylaşım->Gelişmiş`. * İnternet üzerinden erişimi etkinleştirmek istiyorsanız, kurulum yapmalısınız sunucudaki kullanıcı hesaplarını kullanın ve erişim vermek için :option:`--username` ve :option:`--password` seçeneklerini :command:`calibredb` için kullanın. Kullanıcı hesaplarını oluşturmak için ``--enable-auth` seçeneğini kullanarak ve --manage-users seçeneğini kullanarak calibre-server için kullanıcı kimlik doğrulamasını ayarlayabilirsiniz. sunucuyu ana calibre programından çalıştırırken, Tercihler → Ağ üzerinden paylaşım → Kullanıcı adı/şifre iste seçeneğini kullanın.

Çalışan bir İçerik sunucusuna bağlanmak için sunucunun URL’sini şuraya iletin: option:–with-library seçeneği, ayrıntılar ve örnekler için o seçeneğin belgelerine bakın.

- GENEL SEÇENEKLER

- list

- add

- KLASÖRLERDEN EKLE

- remove

- add_format

- remove_format

- show_metadata

- set_metadata

- export

- catalog

- EPUB SEÇENEKLER

- saved_searches

- add_custom_column

- custom_columns

- remove_custom_column

- set_custom

- restore_database

- check_library

- list_categories

- backup_metadata

- clone

- embed_metadata

- search

- fts_index

- fts_search

GENEL SEÇENEKLER

--help, -h

bu yardım iletisini göster ve çık

--library-path, --with-library

Calibre kitaplığının yolu. Varsayılan, ayarlarda saklanan yolu kullanmaktır. Uzak kitaplıklarda eylemler gerçekleştirmek için bir calibre İçerik sunucusuna da bağlanabilirsiniz. Bunu yapmak için şu biçimde bir URL kullanın: http://sunucuadı:port/#kitaplık_no örneğin, http://localhost:8080/#mylibrary.kitaplık_no, İçerik sunucusunda bağlanmak istediğiniz kitaplığın kitaplık kimliğidir. Sunucuda bulunan kitaplık kimliklerinin bir listesini almak için -'nin özel kimlik numarası değerini kullanabilirsiniz. Bir İçerik sunucusu üzerinden erişimin nasıl kurulacağıyla ilgili ayrıntılar için bakınız https://manual.calibre-ebook.com/tr/generated/tr/calibredb.html.

--password

Calibre İçerik sunucusuna bağlanmak için parola. Parolayı standart girdiden okumak için özel değeri kullanın: <stdin>. Parolayı bir dosyadan okumak için şunu kullanın: <f:/path/to/file> (ör. <f: ardından dosyanın tam yolu ve sonunda >). Yukarıdaki köşeli parantezler gereklidir, bunlardan kaçmayı unutmayın veya kabuğunuz için tırnak işaretleri kullanın.

--timeout

Ağ üzerinden bir calibre kitaplığına bağlanırken saniye cinsinden zaman aşımı. Varsayılan iki dakikadır.

--username

Calibre içerik sunucusuna bağlanmak için kullanıcı adı

--version

programın sürüm numarasını göster ve çık

list

calibredb list [seçenekler]

Calibre veritabanındaki kullanılabilir kitapları listele.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--ascending

Sonuçları artan şekilde sırala

--fields, -f

Veritabanındaki kitaplar listelenirken görüntülenecek alanlar. Alanların virgülle ayrılmış bir listesi olmalıdır. Erişilebilir alanlar: author_sort, authors, comments, cover, formats, identifiers, isbn, languages, last_modified, pubdate, publisher, rating, series, series_index, size, tags, template, timestamp, title, uuid Öntanımlı: title,authors. "Tümü" özel alanı tüm alanları seçmek için kullanılabilir. Yukarıdaki tümleşik alanlara ek olarak, *alan_adı şeklinde kişisel alanlara da erişilebilir. Mesela, #onaylama kişisel alanı için şu ismi kullanın: *onaylama

--for-machine

Makine ayrıştırması için daha uygun olan JSON biçiminde çıktı üret. Satır genişliği ve ayraç seçeneklerinin yok sayılmasına neden olur.

--limit

Gösterilecek en fazla sonuç sayısı. Varsayılan: tümü

--line-width, -w

Girdideki bir satırın en fazla genişliği. Varsayılanı ekranın boyutunu algılamaktır.

--prefix

Tüm dosya yolları için önek. Varsayılan kitaplık dizininin mutlak yoludur.

--search, -s

Sonuçları arama sorgusuna göre süzün. Arama sorgusunun biçimi için lütfen Kullanıcı Kılavuzundaki ilgili belgelendirmeye bakın. Varsayılan olarak süzme işlemi yapılmaz.

--separator

Alanları ayırmak için kullanılan karakter dizisi. Varsayılan olarak boşluk karakteridir.

--sort-by

Sonuçları sıralamak için kullanılacak alan Birden fazla alanı virgülle ayırarak belirtebilirsiniz. Kullanılabilir alanlar: author_sort, authors, comments, cover, formats, identifiers, isbn, languages, last_modified, pubdate, publisher, rating, series, series_index, size, tags, template, timestamp, title, uuid Varsayılan: id

--template

Alan listesinde "template" varsa çalıştırılacak şablon. Varsayılan: Yok

--template_file, -t

Alan listesinde "template" varsa çalıştırılacak şablonu içeren dosyanın yolu. Varsayılan: Yok

--template_heading

Şablon sütununu için başlık. Varsayılan: template. --for-machine seçeneği ayarlanmışsa bu seçenek göz ardı edilir

add

%p rog add [seçenekler] dosya1 dosya2 dosya3 ... Belirtilen dosyaları veritabanına kitap olarak ekleyin. Ayrıca klasörleri de belirtebilirsiniz, klasörlerle ilgili aşağıdaki seçeneklere bakınız.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--authors, -a

Eklenen kitap(lar)ın yazarlarını ayarla

--automerge, -m

Benzer başlıklara ve yazarlara sahip kitaplar bulunursa, gelen formatları (dosyaları) otomatik olarak mevcut kitap kayıtlarıyla birleştirin. "ignore" değeri, yinelenen biçimlerin atıldığı anlamına gelir. "overwrite" değeri, yeni eklenen dosyalarla kitaplıktaki yinelenen biçimlerin üzerine yazılacağı anlamına gelir. "new_record" değeri, yinelenen biçimlerin yeni bir kitap kaydına yerleştirildiği anlamına gelir.

--cover, -c

Eklenen kitap için kullanılacak kapağın yolu

--duplicates, -d

Zaten var olsalar bile veritabanına kitap ekleyin. Karşılaştırma kitap adları ve yazarlara göre yapılmıştır. --automerge seçeneğinin öncelikli olduğunu unutmayın.

--empty, -e

Boş bir kitap ekle (biçimi olmayan bir kitap)

--identifier, -I

Bu kitap için tanımlayıcılar ekleyin, e.g. -I asin:XXX -I isbn:YYY

--isbn, -i

Eklenen kitap(lar)ın ISBN'lerini ayarla

--languages, -l

Virgülle ayrılmış dillerin listesi (bazı dil isimleri anlaşılamayacağı için en iyisi ISO639 dil kodlarını kullanmak olacaktır)

--series, -s

Eklenen kitap(lar)ın serilerini ayarla

--series-index, -S

Eklenen kitap(lar)ın seri numaralarını ayarla

--tags, -T

Eklenen kitap(lar)ın etiketlerini ayarla

--title, -t

Eklenen kitapların adlarını ayarla

KLASÖRLERDEN EKLE

Klasörlerden kitap eklemeyi kontrol etme seçenekleri. Varsayılan olarak yalnızca bilinen e-kitap dosya türlerinin uzantılarına sahip dosyalar eklenir.

--add

Bilinen bir e-kitap dosyası türünde olmasalar bile, dosyalar için klasörler taranırken bir dosya adı (glob) kalıbı, bu kalıpla eşleşen dosyalar eklenecektir. Birden çok desen için birden çok kez belirtilebilir.

--ignore

Bir dosya adı (glob) kalıbı, dosyalar için klasörler taranırken bu kalıpla eşleşen dosyalar yok sayılır. Birden çok desen için birden çok kez belirtilebilir. Örneğin: *.pdf, tüm PDF dosyalarını yoksayar

--one-book-per-directory, -1

Her klasörün yalnızca tek bir mantıksal kitabı olduğunu ve o klasör içindeki tüm dosyaların o kitabın farklı e-kitap biçimleri olduğunu varsay

--recurse, -r

Klasörleri, alt klasörleri ile birlikte işle

remove

calibredb kimlikleri kaldır

Kimlikleri ile belirtilen kitapları veri tabanından sil. Kimlikler, kimlik numaralarının virgülle ayrılmış listesi olmalıdır (arama komutuyla kimlik numaralarını alabilirsiniz). Örneğin, 23,34,57-85 (aralık belirtirken, aralıkta son numara dahil edilmez).

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--permanent

Geri Dönüşüm Kutusu kullanmayın

add_format

calibredb add_format [seçenekler] id e-kitap_file

e-kitap_file’ndaki e-kitabı kimliği ile tanımlanan mantıksal kitap için kullanılabilir biçime ekle. Arama komutunu kullanarak kimlik alabilirsiniz. Biçim zaten mevcutsa, yer değiştirme seçeneği belirtilmediği takdirde yer değiştirilir.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--as-extra-data-file

Dosyayı ekstra bir veri dosyası olarak kitaba ekle, bir ekitap formatı değildir

--dont-replace

Mevcut biçimi değiştirmeyin

remove_format

calibredb remove_format [seçenekler] id fmt

id ile tanımlanan mantıksal kitaptan fmt biçimini kaldır. Arama komutunu kullanarak id numarasını alabilirsiniz. fmt LRF veya TXT veya EPUB gibi bir dosya uzantısı olmalıdır. Mantıksal kitabın kullanılabilir fmt’si yoksa, hiçbir şey yapma.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

show_metadata

calibredb show_metadata [seçenekler] id

id ile tanımlanan kitap için Calibre veri tabanında kayıtlı metadata’yı göster. id, arama komutuyla gelen id numarasıdır.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--as-opf

Üstbilgiyi OPF formunda yazdır (XML)

set_metadata

calibredb set_metadata [seçenekler] kitap_kimlik_no [/yol/üstveri.opf]

OPF dosyası üstveri.opf’tan kitap_kimlik_no tarafından tanımlanan kitap için calibre veritabanında saklanan üstverileri ayarlayın. kitap_kimlik_no, search komutundan bir kitap kimlik numarasıdır. show_metadata komutunda –as-opf parametresini kullanarak OPF formatı hakkında hızlı bir fikir edinebilirsiniz. Ayrıca –field seçeneğiyle bireysel alanların üstverilerini de ayarlayabilirsiniz. –field seçeneğini kullanırsanız, bir OPF dosyası belirtmenize gerek yoktur.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--field, -f

Ayarlanacak alan. Biçimi alan_ismi:değer şeklindedir, örneğin: --field etiketler:etiket1,etiket2. Tüm alan isimlerinin listesini almak için --list-fields kullanın. Birden çok alan ayarlamak için bu seçeneği birden fazla kere kullanabilirsiniz. Not: Diller için ISO639 dil kodlarını kullanmalısınız (örn. İngilizce için en, Fransızca için fr ve benzeri). Tanımlayıcılar için, söz dizimi --field identifiers:isbn:XXXX,doi:YYYYY şeklindedir. Bool (evet/hayır) değerler için true ve false veya yes veya no kullanın.

--list-fields, -l

--field seçeneğiyle kullanılabilecek metadata alan isimlerini listeleyin

export

calibredb export [seçenekler] kimlik numaraları

Kimlik numaraları ile belirtilen kitapları (virgülle ayrılmış bir liste) dosya sistemine aktarın. Dışa aktarma işlemi kitabın tüm formatlarını, kapağını ve meta verilerini (bir OPF dosyasında) kaydeder. Kitapla ilişkili tüm ekstra veri dosyaları da kaydedilir. Kimlik numaralarını arama komutundan alabilirsiniz.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--all

Kimlik listesine bakmadan tüm kitapları dışa aktar.

--dont-asciiize

Have calibre convert all non English characters into English equivalents for the file names. This is useful if saving to a legacy filesystem without full support for Unicode filenames. Bu anahtar bu davranışı kapatacaktır.

--dont-save-cover

Normally, calibre will save the cover in a separate file along with the actual e-book files. Bu anahtar bu davranışı kapatacaktır.

--dont-save-extra-files

Save any data files associated with the book when saving the book Bu anahtar bu davranışı kapatacaktır.

--dont-update-metadata

Normal olarak Calibre üst veriyi Calibre kitaplığındaki kaydedilmiş dosyalardan günceller. Diske kaydetmeyi yavaşlatır. Bu anahtar bu davranışı kapatacaktır.

--dont-write-opf

Normal olarak Calibre üst veriyi gerçek e-kitap dosyalarının yanına ayrı bir OPF dosyasına yazar. Bu anahtar bu davranışı kapatacaktır.

--formats

Kitapların kaydedileceği biçimlerin virgülle ayrılmış listesi. Varsayılan olarak tüm biçimlerde kaydedilir.

--progress

Rapor durumu

--replace-whitespace

Boşlukları alt çizgi ile değiştir.

--single-dir

Tüm kitapları tek bir klasöre aktarın

--template

The template to control the filename and folder structure of the saved files. Default is "{author_sort}/{title}/{title} - {authors}" which will save books into a per-author subfolder with filenames containing title and author. Available controls are: {author_sort, authors, id, isbn, languages, last_modified, pubdate, publisher, rating, series, series_index, tags, timestamp, title}

--timefmt

Tarihlerin görüntüleneceği biçim. %d - gün, %b - ay, %m - ay numarası, %Y - yıl. Varsayılan: %b, %Y

--to-dir

Kitapları belirtilen dizine aktarın. Varsayılan .

--to-lowercase

Yolları küçük harflere dönüştür.

catalog

calibredb catalog /path/to/destination.(csv|epub|mobi|xml...) [options]

Export a catalog in format specified by path/to/destination extension. Options control how entries are displayed in the generated catalog output. Note that different catalog formats support different sets of options. To see the different options, specify the name of the output file and then the –help option.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--ids, -i

Kataloglanacak veritabanı kimliklerinin virgülle ayrılmış listesi. Kullanılırsa, --search yok sayılır. Varsayılan: all

--search, -s

Sonuçları arama sorgusuna göre sırala. Arama sorgusu biçimi için, lütfen Kullanıcı Kılavuzundaki aramayla ilgili belgeye bakın. Varsayılan: filtre yok

--verbose, -v

Ayrıntılı çıktı bilgisi göster. Hata ayıklamada faydalıdır

EPUB SEÇENEKLER

--catalog-title

Title of generated catalog used as title in metadata. Default: 'My Books' Applies to: AZW3, EPUB, MOBI output formats

--cross-reference-authors

Create cross-references in Authors section for books with multiple authors. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

--debug-pipeline

Save the output from different stages of the conversion pipeline to the specified folder. Useful if you are unsure at which stage of the conversion process a bug is occurring. Default: 'None' Applies to: AZW3, EPUB, MOBI output formats

--exclude-genre

Regex describing tags to exclude as genres. Default: '[.+]|^+$' excludes bracketed tags, e.g. '[Project Gutenberg]', and '+', the default tag for read books. Applies to: AZW3, EPUB, MOBI output formats

--exclusion-rules

Specifies the rules used to exclude books from the generated catalog. The model for an exclusion rule is either ('<rule name>','Tags','<comma-separated list of tags>') or ('<rule name>','<custom column>','<pattern>'). For example: (('Archived books','#status','Archived'),) will exclude a book with a value of 'Archived' in the custom column 'status'. When multiple rules are defined, all rules will be applied. Default: "(('Catalogs','Tags','Catalog'),)" Applies to: AZW3, EPUB, MOBI output formats

--generate-authors

Include 'Authors' section in catalog. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

--generate-descriptions

Include 'Descriptions' section in catalog. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

--generate-genres

Include 'Genres' section in catalog. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

--generate-recently-added

Include 'Recently Added' section in catalog. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

--generate-series

Include 'Series' section in catalog. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

--generate-titles

Include 'Titles' section in catalog. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

--genre-source-field

Source field for 'Genres' section. Default: 'Etiketler' Applies to: AZW3, EPUB, MOBI output formats

--header-note-source-field

Custom field containing note text to insert in Description header. Default: '' Applies to: AZW3, EPUB, MOBI output formats

--merge-comments-rule

#<custom field>:[before|after]:[True|False] specifying: <custom field> Custom field containing notes to merge with comments [before|after] Placement of notes with respect to comments [True|False] - A horizontal rule is inserted between notes and comments Default: '::' Applies to: AZW3, EPUB, MOBI output formats

--output-profile

Specifies the output profile. In some cases, an output profile is required to optimize the catalog for the device. For example, 'kindle' or 'kindle_dx' creates a structured Table of Contents with Sections and Articles. Default: 'None' Applies to: AZW3, EPUB, MOBI output formats

--prefix-rules

Specifies the rules used to include prefixes indicating read books, wishlist items and other user-specified prefixes. The model for a prefix rule is ('<rule name>','<source field>','<pattern>','<prefix>'). When multiple rules are defined, the first matching rule will be used. Default: "(('Read books','tags','+','✓'),('Wishlist item','tags','Wishlist','×'))" Applies to: AZW3, EPUB, MOBI output formats

--preset

Use a named preset created with the GUI catalog builder. A preset specifies all settings for building a catalog. Default: 'None' Applies to: AZW3, EPUB, MOBI output formats

--thumb-width

Size hint (in inches) for book covers in catalog. Range: 1.0 - 2.0 Default: '1.0' Applies to: AZW3, EPUB, MOBI output formats

--use-existing-cover

Replace existing cover when generating the catalog. Default: 'False' Applies to: AZW3, EPUB, MOBI output formats

saved_searches

calibredb saved_searches [seçenekler] (list|add|remove)

Bu veritabanında saklanan kayıtlı aramaları yönetin. Zaten var olan bir ada sahip bir sorgu eklemeye çalışırsanız, bu sorgu değiştirilecektir.

Ekleme için sözdizimi :calibredb saved_searches add search_name search_expression

Kaldırmak için sözdizimi: calibredb saved_searches remove search_name

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

add_custom_column

calibredb add_custom_column [seçenekler] etiket isim veritürü

Bir özel sütun oluştur. etiket sütunun makinenin anlayacağı ismidir. Boşluk veya sütun içermemelidir. name sütunun kullanıcı dostu ismidir. veritürü şunlardan biridir: bool, comments, composite, datetime, enumeration, float, int, rating, series, text

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--display

Bu sütundaki verinin nasıl yorumlanacağının özelleştirilmesi için seçenekler sözlüğü. Bu bir JSON karater dizisidir. Sıralama sütunları için, --display"{\ "enum_values\ ":[\ "val1\ ", \ "val2\ "]}" kullanındisplay değişkenine verilebilecek bir çok seçenek vardır. Sütun türüne göre seçenekler: composite: composite_template, composite_sort, make_category,contains_html, use_decorations datetime: date_format enumeration: enum_values, enum_colors, use_decorations int, float: number_format text: is_names, use_decorations Uygun kombinasyonlar bulmanın en iyi yolu grafik arayüzden uygun türde bir özel sütun oluşturmak ardından kitap için OPF yedeğine bakmaktır (Sütun eklendikten sonra yeni bir OPF oluştuğundan emin olun). OPF'de yeni sütun için "display" JSON'unu göreceksiniz.

--is-multiple

Bu sütun etiket benzeri verileri (virgülle ayrılmış değerleri) içerir. Sadece veri tipi metin olduğunda uygulanır.

custom_columns

calibredb özel_sütunlar [seçenekler]

Kullanılabilir özel sütunları listele. Sütun etiketlerini ve kimliklerini gösterir.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--details, -d

Her sütun için ayrıntıları göster.

remove_custom_column

calibredb remove_custom_column [seçenekler] etiket

Etiket tarafından belirlenmiş kişisel sütunu kaldır. Kullanılabilir sütunları custom_columns komutuyla görebilirsiniz.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--force, -f

Onaylama sorma

set_custom

calibredb set_custom [seçenekler] sütun kimlik değeri

Kimlik numarası tarafından tanımlanan kitap için özel bir sütunun değerini ayarlayın. search komutunu kullanarak kimliklerin bir listesini alabilirsiniz. Özel sütun adlarının bir listesiniise custom_columnskomutu ile alabilrsiniz.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--append, -a

Sütun birden çok değer saklıyorsa, belirtilen değerleri üzerine yazmak yerine mevcut olanlara ekleyin.

restore_database

calibredb restore_database [seçenekler]

Bu veritabanını calibre kitaplığının herklasöründeki OPF dosyalarında saklanan üstverilerden geri yükleyin. Bu, metadata.db dosyanız bozuksa kullanışlıdır.

UYARI: Bu komut, veritabanınızı tamamen yeniden oluşturur. Kaydedilmiş tüm aramaları, kullanıcı kategorilerini, santralleri, kitap başına kaydedilen dönüştürme ayarlarını ve özel tarifleri kaybedeceksiniz. Geri yüklenen üstveri, yalnızca OPF dosyalarında bulunanlar kadar doğru olacaktır.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--really-do-it, -r

Kurtarmayı gerçekleştir. Komut bu seçeneği seçmediğinizde çalışmayacaktır.

check_library

calibredb check_library [seçenekler]

Bir kitaplığı temsil eden dosya sisteminde bazı kontroller gerçekleştirir. Raporlar: invalid_titles, extra_titles, invalid_authors, extra_authors, missing_formats, extra_formats, extra_files, missing_covers, extra_covers, failed_folders

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--csv, -c

CSV biçiminde çıktı

--ignore_extensions, -e

Yoksayılacak eklentilerin virgülle ayrılmış listesi. Varsayılan: tümü

--ignore_names, -n

Yoksayılacak isimlerin virgülle ayrılmış listesi. Default: tümü

--report, -r

Raporların virgülle ayrılmış listesi. Varsayılan: tümü

--vacuum-fts-db

Tam metin arama veri tabanını boşaltın. Bu, veri tabanının boyutuna bağlı olarak çok yavaş ve bellek açısından yoğun olabilir.

list_categories

calibredb list_categories [seçenekler]

Veritabanındaki kategori bilgilerinin bir raporunu oluşturur. Bilgi, etiket tarayıcısında gösterilene eşdeğerdir.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--categories, -r

Virgülle ayrılmış aranacak kategori isimleri listesi. Öntanımlı: tümü

--csv, -c

CSV biçiminde çıktı

--dialect

Üretilecek CSV dosyası türü. Seçilebilecekler: excel, excel-tab, unix

--item_count, -i

Kategori içindeki öğe başına sayı yerine kategori içindeki öğe sayısını çıktıla

--width, -w

Girdideki bir satırın en fazla genişliği. Varsayılanı ekranın boyutunu algılamaktır.

backup_metadata

calibredb backup_metadata [seçenekler]

Veritabanında depolanan üstverileri her bir kitap klasöründeki ayrı OPF dosyalarına yedekleyin. Bu normalde otomatik olarak gerçekleşir, ancak –all seçeneğiyle OPF dosyalarının yeniden oluşturulmasını zorlamak için bukomutu çalıştırabilirsiniz.

Not: her üstveri değişikliğinde OPF dosyaları otomatik olarak yedeklendiğinden normalde bunu yapmanıza gerek yoktur

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--all

Normal olarak bu komut sadece tarihi geçmiş OPF dosyaları içeren kitaplarda çalışır. Bu seçenek onu tüm kitaplarda çalıştırır.

clone

calibredb clone path/to/new/library

Geçerli kitaplığın bir kopyasını oluştur. Bu, geçerli kitaplıkla aynı özel sütunlara, Sanal kitaplıklara ve diğer ayarlara sahip yeni, boş bir kitaplık oluşturur.

Çoğaltılan kitaplık kitap içermez. Tüm kitaplar dahil tam bir kopya oluşturmak istiyorsanız, kitaplık klasörünü kopyalamak için dosya sistemi aracınızı kullanınız.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

embed_metadata

calibredb embed_metadata [seçenekler] kitap_kimliği

Calibre kitaplığında saklanan asıl kitap sayfalarını Calibre veri tabanındaki metadata ile güncelle. Normalde, metadata yalnızca Calibre’den dışa aktarım yapılırken güncellenir, bu komut dosyaları olduğu gibi değiştirmek istiyorsanız faydalıdır. Değişik dosya biçimlerinin değişik miktarda metadata desteklediğini unutmayın. kitap_kimliği için özel bir değer olan ‘all’ kullanarak tüm kitaplarda metadata güncelleyebilirsiniz. Ayrıca birden çok kitap kimliğini boşluklarla ayırarak ve kimlik aralıklarını tire ile ayırarak belirtebilirsiniz. Örneğin: calibredb embed_metadata 1 2 10-15 23

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--only-formats, -f

Sadece belirtilen biçimlerdeki dosyaların üstbilgilerini güncelle. Birden çok biçim için birden çok defa tanımlama yapmanız gerekir. Varsayılan olarak tüm biçimler güncellenir.

search

calibredb search [seçenekler] arama ifadesi

Belirtilen arama terimini kitaplıkta arayın ve arama ifadesiyle eşleşen kitap kimlik numaralarının virgülle ayrılmışlistesini döndürün. Çıktı biçimibir kimlik numarası listesini girdi olarak kabul eden diğer komutları beslemek için kullanışlıdır.

Arama ifadesi calibre’nin güçlü arama sorgusudilindeki herhangi bir şey olabilir, örneğin: calibredb search author:asimov ‘title:”i robot”’

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--limit, -l

Azami döndürülecek sonuç sayısı. Varsayılan tüm sonuçlardır.

fts_index

calibredb fts_index [seçenekler] enable/disable/status/reindex

Tam metin arama indeksleme işlemini kontrol edin.

enable Bu kitaplık için FTS indekslemeyi açar disable Bu kitaplık için FTS dizini indekslemeyi kapatır status Geçerli dizin oluşturma durumunu gösterir reindex Belirli kitapları veya tüm kitaplığı yeniden dizinlemek için kullanılabilir. Belirli kitapları yeniden indekslemek için kitap kimliklerini reindex komutundan sonra ek argümanlar olarak belirtin. Hiçbir kitap kimliği belirtilmezse tüm kitaplık yeniden indekslenir.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--indexing-speed

İndeksleme hızı. Tüm bilgisayar kaynaklarınızı kullanarak hızlı indeksleme için hızlı, daha az kaynak yoğun indeksleme için yavaş kullanın. Her çalıştırmadan sonra hızın yavaşlamak için sıfırlandığını unutmayın.

--wait-for-completion

Tüm kitaplar indekslenene kadar bekleyin, indeksleme ilerlemesini periyodik olarak gösterin

fts_search

calibredb fts_search [seçenekler] arama ifadesi

Kitaplığın tamamında veya bir alt kümesinde tam metin araması yapın.

Whenever you pass arguments to calibredb that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

--do-not-match-on-related-words

İlgili kelimelerle değil, yalnızca tam kelimelerle eşleştirin. Yani düzeltmek, düzeltme ile eşleşmeyecektir.

--include-snippets

Her eşleşmeyi çevreleyen metnin parçacıklarını ekleyin. Not bu aramayı çok daha yavaş hale getirir.

--indexing-threshold

Aramaya izin verilmeden önce, yüzde olarak kitaplığın ne kadarının dizine eklenmesi gerektiği. Varsayılanlar 90'dır

--match-end-marker

Bu işaretçi eşleşen bir parçacığın içindeki eşleşen bir kelimenin sonunu belirtmek için kullanılır

--match-start-marker

Bu işaretçi bir parçacığın içindeki eşleşen bir kelimenin başlangıcını belirtmek için kullanılır

--output-format

Arama sonuçlarının çıktı biçimi, düz metin için "text" veya JSON çıktısı için "json" şeklindedir.

--restrict-to

Bir arama ifadesi veya kimlik numaraları kullanarak aranan kitapları kısıtlayın. Örneğin: kimlik numaralarına göre kısıtlamak için ids:1,2,3 veya foo etiketine sahip kitaplara göre kısıtlamak için search:tag:foo.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- ebook-convert

ebook-convert

ebook-convert girdi_dosyası çıktı_dosyası [seçenekler]

Bir e-kitabı bir biçimden diğerine dönüştürün.

girdi_dosyası girdi, çıktı_dosyası ise çıktıdır. Her ikisi de komutun ilk iki argümanı olarak belirtilmelidir.

Çıktı e-kitap formatı, çıktı_dosyası dosya uzantısından tahmin edilir. çıktı_dosyası ayrıca .EXT özel biçiminde olabilir; burada EXT, çıktı dosyası uzantısıdır. Bu durumda çıktı dosyasının adı girdi dosyasının adından türetilir. Dosya adlarının kısa çizgi ile başlamaması gerektiğini unutmayın. Son olarak, çıktı_dosyası’nin uzantısı yoksa, o zaman bir klasör olarak kabul edilir ve bu klasöre HTML dosyalarından oluşan bir “açık e-kitap” (OEB) yazılır. Bu dosyalar, normalde çıkış eklentisine aktarılacak olan dosyalardır.

Giriş ve çıkış dosyasını belirledikten sonra, çeşitli seçenekler belirleyerek dönüştürmeyi özelleştirebilirsiniz. Kullanılabilir seçenekler, giriş ve çıkış dosyası türlerine bağlıdır. Bunlarla ilgili yardım almak için giriş ve çıkış dosyasını belirtin ve ardından -h seçeneğini kullanın.

Dönüştürme sistemi dökümanları için bakınız: E-kitap dönüşümü

Whenever you pass arguments to ebook-convert that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

Seçenekler ve seçeneklerin varsayılan değerleri girdi ve çıktı biçimlerine göre değişiklik gösterir, her zaman şu şekilde kontrol etmelisiniz:

ebook-convert myfile.input_format myfile.output_format -h

Aşağıda tüm dönüştürmeler için ortak seçenekler ve her girdi ve çıktıya özel seçenekler bulunabilir.

- GİRDİ AYARLARI

- ÇIKTI AYARLARI

- GÖR ve HİSSET

- SEZGİSEL İŞLEME

- BUL ve DEĞİŞTİR

- YAPI ALGILAMA

- İÇİNDEKİLER

- ÜSTBİLGİ

- HATA AYIKLA

--help, -h

bu yardım iletisini göster ve çık

--input-profile

Girdi profilini belirleyin. Bu profil dönüştürme sistemine girdi dosyasındaki çeşitli bilgileri nasıl kullanacağı bilgisini verir. örneğin çözüünürlüğe bağlı uzunluk( yani piksel cinsinden uzunluk) Seçimler şunlar: cybookg3, cybook_opus, default, hanlinv3, hanlinv5, illiad, irexdr1000, irexdr800, kindle, msreader, mobipocket, nook, sony, sony300, sony900

--list-recipes

Yerleşik tarif adlarını listeleyin. Yerleşik bir tariften şu şekilde bir e-kitap oluşturabilirsiniz: ebook-convert "TarifAdı.recipe" ÇıktıDosyası.epub

--output-profile

Çıktı profilini belirtin. Çıktı profili dönüştürme sistemine oluşturulan belgenin belirtilen aygıta göre nasıl en iyi hale getirileceğini söyler. Bazı durumlarda, çıktı profili çıktıyı belli bir aygıt için en iyi hale getirmek için kullanılabilir, ama bu genellikle gereksizdir. Seçenekler şunlardır:cybookg3, cybook_opus, default, generic_eink, generic_eink_large, generic_eink_hd, hanlinv3, hanlinv5, illiad, ipad, ipad3, irexdr1000, irexdr800, jetbook5, kindle, kindle_dx, kindle_fire, kindle_oasis, kindle_pw, kindle_pw3, kindle_scribe, kindle_voyage, kobo, msreader, mobipocket, nook, nook_color, nook_hd_plus, pocketbook_inkpad3, pocketbook_lux, pocketbook_hd, pocketbook_900, pocketbook_pro_912, galaxy, sony, sony300, sony900, sony-landscape, sonyt3, tablet

--version

programın sürüm numarasını göster ve çık

GİRDİ AYARLARI

Girdi dosyası mobi ‘iın işlenme sürecinin kontrol seçenekleri

--input-encoding

Girdi dökümanındaki dil kodlamasını belirtin. Belirtilirse, bu seçenek dökümanın kendisinde belirtilmiş herhangi bir kodlamanın üzerine yazar. Bilhassa kodlama belirtmeyen veya hatalı kodlama belirtilmiş dökümanlar için kullanışlıdır.

ÇIKTI AYARLARI

Çıktı dosyası epub ‘nın işlenme sürecinin kontrol seçenekleri

--dont-split-on-page-breaks

Sayfa sonlarında bölmeyi kapatın. Normalde, giriş dosyaları her sayfa sonunda otomatik olarak iki dosyaya bölünür. Bu, daha hızlı ve daha az kaynakla ayrıştırılabilen bir e-kitap çıktısı verir. Ancak, bölme yavaştır ve kaynak dosyanız çok sayıda sayfa sonu içeriyorsa, sayfa sonlarında bölmeyi kapatmalısınız.

--epub-flatten

EPUB dosyasını FBReaderJ ile kullanmayı düşünüyorsanız bu özellik gereklidir. Böylece tüm dosyaları, dosya sisteminin en üst seviyesine koyarak düz bir dosya sistemi yaratır.

--epub-inline-toc

Ana kitap içeriğinin parçası olarak görünecek bir satır arası İçindekiler ekle.

--epub-max-image-size

Maksimum görüntü boyutu (genişlik x yükseklik). none değeri, çıktı profilindeki ekran boyutunu kullanmak anlamına gelir. profile değeri, maksimum boyutun belirtilmediği anlamına gelir. Örneğin, 100x200 değeri, tüm görüntülerin yeniden boyutlandırılmasına neden olur, böylece genişlikleri 100 pikselden ve yükseklikleri 200 pikselden fazla olamaz. Bunun yalnızca gerçek görüntü dosyalarının boyutunu etkilediğini unutmayın. Herhangi bir görüntü, belgede kendisine uygulanan stile bağlı olarak farklı bir boyutta işlenebilir.

--epub-toc-at-end

Eklenen satır arası içindekileri kitabın başı yerine sonuna koy.

--epub-version

Oluşturulacak EPUB dosyasının sürümü. EPUB 2 en geniş uyumlu olanıdır, EPUB 3'ü yalnızca gerçekten ihtiyacınız olduğunu biliyorsanız kullanın.

--extract-to

Oluşturulan EPUB dosyasının içeriğini belirtilen klasöre çıkarın. Önce klasörün içeriği silinir, bu yüzden dikkatli olun.

--flow-size

Bu boyutttan büyük bütün HTML dosyalarını küçük parçalara böl (KB olarak). Çoğu ePUB okuyucu büyük boyuttaki dosyaları okuyamadığı için bu gereklidir. Öntanımlı 260KB boyutu Adobe Digital Edition'lar için gereklidir. Boyut temelli bölmeyi devre dışı bırakmak için 0 olarak ayarlayın.

--no-default-epub-cover

Normal şartlarda, eklediğiniz kitabın kapağı yoksa ve siz de ayrıca bir kapak belirlemediyseniz; kitap adı ve yazar adı vs bilgilerden bir öntanımlı kapak oluşturulur. Bu ayar öntanımlı kapak oluşturmayı devre dışı bırakır.

--no-svg-cover

Kitap kapağı için SVG kullanma. Bu seçeneği yalnızca EPUB'ınızı iPhone ya da JetBook Lite gibi SVG desteklemeyen bir cihazda kullanacaksanız seçin. Bu seçenek seçili değilse, bu cihazlar kitap kapağı yerine boş bir sayfa görüntüleyecektir.

--preserve-cover-aspect-ratio

Kitap kapağı olarak SVG kullandığınızda bu seçenek, kitap kapağının müsait ekran alanına en/boy oranının korunarak ölçeklenmesini sağlar. Yani bazı durumlarda resmin kenarlarında beyaz alanlar bulunabilir ancak resim asla bozulmaz. Bu seçenek seçili değilse resim bir miktar bozulabilir ama kenarlarda beyaz alanlar olmaz.

--pretty-print

Eğer belirtilirse, çıktı ektentisi en okunabilir şekilde bir çıktı yaratmaya çalışacaktır. Bazı çıktı eklentilerinde hiç bir etkisi olmayabilir.

--toc-title

Oluşturulan İçindekiler (TOC) bölümü için başlık.

GÖR ve HİSSET

Çıktı dosyasının görünümünü ayarlamak için kullanılabilecek ayarlar

--asciiize

Transliterate Unicode characters to an ASCII representation. Use with care because this will replace Unicode characters with ASCII. For instance it will replace "Pelé" with "Pele". Also, note that in cases where there are multiple representations of a character (characters shared by Chinese and Japanese for instance) the representation based on the current calibre interface language will be used.

--base-font-size

The base font size in pts. All font sizes in the produced book will be rescaled based on this size. By choosing a larger size you can make the fonts in the output bigger and vice versa. By default, when the value is zero, the base font size is chosen based on the output profile you chose.

--change-justification

Metnin yaslama ayarlarını değiştir. "left" değeri tüm çift tarafa da dayalı (justified) yazıyı sola dayalı yapar. "justify" ise çift tarafa dayalı olmayan yazıları çift tarafa dayalı yapar. "original" değeri ise kaynak dosyadaki değeri korur. Tüm çıktı formatlarının çift tarafa dayalı formatı desteklemediiğini unutmayın.

--disable-font-rescaling

Tüm yazı tiplerinin yeniden boyutlandırılmasını etkisizleştir.

--embed-all-fonts

Henüz gömülmemiş ama girdi belgesinde başvurulan tüm yazı tiplerini göm. Bu sisteminizi yazı tipleri için arar, ve bulursa, bulunanlar gömülür. Gömme yalnızca dönüştürdüğünüz yazı tipi gömülü yazı tiplerini destekliyorsa çalışır, örneğin EPUB, AZW3, DOCX veya PDF. Lütfen bu belgede kullanılan gömülü yazı tiplerini kullanmak için gerekli izinleriniz olduğundan emin olun.

--embed-font-family

Belirtilen yazı tipi ailesini kitaba göm. Bu kitap için kullanılan "temel" yazı tipini belirtir. Girdi belgesi kendi yazı tiplerini belirtmişse, bu temel yazı tipini ezebilir. Filtre biçim bilgisi seçeneğini kullanarak yazı tiplerini girdi belgesinden çıkarabilirsiniz. Yazı tipi gömmenin yalnızca bazı çıktı formatlarında çalıştığını unutmayın, özellikle EPUB, AZW3 ve DOCX.

--expand-css

Calibre varsayılan olarak kenar boşluğu, dolgu, kenarlık vb. gibi çeşitli CSS özellikleri için kısayol formunu kullanacaktır. Bu seçenek bunun yerine tam genişletilmiş formu kullanmasına neden olur. Nook steno CSS'yi işleyemediğinden, çıktı profili Nook profillerinden birine ayarlanmış olarak EPUB dosyaları oluştururken CSS'nin her zaman genişletildiğini unutmayın.

--extra-css

CSS biçim sayfasına veya ham CSS'ye olan yol. Bu CSS, kaynak dosyadaki biçim kurallarına eklenecek, bu sayede bu kuralların yerine kullanılabilecektir.

--filter-css

Tüm CSS biçim kurallarından çıkarılacak CSS özelliklerinin virgülle ayrılmış listesi. Bazı biçim bilgisinin varlığının aygıtınızda üzerine yazılmasını engellediği durumlarda kullanışlıdır. Örneğin: font-family,color,margin-left,margin-right

--font-size-mapping

CSS yazı tipi isimlerinden yazı tipi boyutlarına nokta olarak haritalama. Örnek bir ayar şu şekildedir 12,12,14,16,18,20,22,24. Bunlar xx-small'dan xx-large'a boyutların haritasıdır, son boyut dev yazı tipleri içindir. Yazı tipi yeniden ölçekleme algoritması bu boyutları kullanarak yazı tiplerini akıllıca yeniden boyutlandırır. Varsayılan seçtiğiniz çıktı profiline göre bir haritalama kullanmaktır.

--insert-blank-line

Paragraflar arasına boş satır ekle. Eğer kaynak dosyası paragraf (<p> or <div> etiketlerini) içermiyorsa çalışmaz

--insert-blank-line-size

Eklenen boşlukların yüksekliğini "em" cinsinden ayarlayın. Parağraf arası boşluklar ise buradaki değerin iki katı olarak ayarlanacaktır.

--keep-ligatures

Girdi belgesinde mevcut olan bağları koru. Bir bağ, ff, fi, fl vs. gibi bir çift karakterin özel olarak gerçeklenmesine denir. Çoğu okuyucu varsayılan yazı tiplerinde bağları desteklemezler, yani doğru gerçeklemeleri beklenmez. Varsayılan olarak, Calibre bir bağı karşılık gelen normal karakter çiftine çevirir. Bu seçenek ise korunmalarını sağlar.

--line-height

Sayı olarak satır yüksekliği. Ardı ardına gelen satırlar arasındaki boşluğu kontrol eder. Kendi satır yüksekliğini belirlemeyen unsurlara uygulanır. Çoğu durumda minimum satır aralığı tercihi daha kullanışlıdır. Varsayılan olarak herhangi bir satır yüksekliği işlemi yapılmaz.

--linearize-tables

Bazı kötü dizayn edilmiş dökümanlar sayfadaki metnin düzenlemesini kontrol için tablo kullanırlar. Bu sayfalar dönüştürüldüğünde sıklıkla metin sayfadan ve taşıyor ve başka hatalar çıkıyor. Bu seçenek içeriği tablodan çıkaracak ve onu bildiğimiz şekilde sunacak.

--margin-bottom

Alt kenar boşluğunu pt olarak ayarlayın. Varsayılan %darsayılandır. Bunu sıfırdan küçük bir değere ayarlamak, kenar boşluğunun ayarlanmamasına neden olur (orijinal belgedeki kenar boşluğu ayarı korunur). Not: PDF ve DOCX gibi sayfa odaklı formatların öncelikli olan kendi kenar boşluğu ayarları vardır.

--margin-left

Sol kenar boşluğunu pt olarak ayarlayın. Varsayılan %darsayılandır. Bunu sıfırdan küçük bir değere ayarlamak, kenar boşluğunun ayarlanmamasına neden olur (orijinal belgedeki kenar boşluğu ayarı korunur). Not: PDF ve DOCX gibi sayfa odaklı formatların öncelikli olan kendi kenar boşluğu ayarları vardır.

--margin-right

Set the right margin in pts. Default is 5.0. Setting this to less than zero will cause no margin to be set (the margin setting in the original document will be preserved). Note: Page oriented formats such as PDF and DOCX have their own margin settings that take precedence.

--margin-top

Üst kenar boşluğunu pt olarak ayarlayın. Varsayılan %darsayılandır. Bunu sıfırdan küçük bir değere ayarlamak, kenar boşluğunun ayarlanmamasına neden olur (orijinal belgedeki kenar boşluğu ayarı korunur). Not: PDF ve DOCX gibi sayfa odaklı formatların öncelikli olan kendi kenar boşluğu ayarları vardır.

--minimum-line-height

Unsurların hesaplanmış font büyüklüğünün yüzdesi olarak, minimum satır yüksekliği. Calibre bu ayar ile girdi dokümanının ne belirttiğinden bağımsız olarak her unsura bir satır yüksekliği atayacaktır. Devre dışı bırakmak için sıfıra ayarlayın. Varsayılan 120%. Ne yaptığınızı bilmiyorsanız, doğrudan satır yüksekliği belirmek yerine bu ayarı kullanın. Örneğin, bunu 240'a ayarlayarak "2 kat satır aralıklı" metin elde edebilirsiniz.

--remove-paragraph-spacing

Paragraflar arası boşlukları kaldır. Ayrıca paragrafların girintilerini 1,5em olarak ayarla. Eğer kaynak dosyası paragraları(<p> or <div> etiketleri) kullanmamışsa boşluk kaldırma çalışmayacaktır.

--remove-paragraph-spacing-indent-size

Calibre paragraflar arasındaki boş satırları kaldırdığında, paragrafların kolayca ayırt edilebilmesi için otomatik olarak bir paragraf girintisi yapar. Bu seçenek, o girintinin genişliğini (em cinsinden) kontrol eder. Bu değeri negatif olarak ayarlarsanız girdi belgesinde belirlenen paragraf girintisi kullanılır. Yani Calibre paragraf girintisini değiştirmez.

--smarten-punctuation

Convert plain quotes, dashes and ellipsis to their typographically correct equivalents. For details, see https://daringfireball.net/projects/smartypants.

--subset-embedded-fonts

Tüm gömülü yazı tiplerini alt kümeye al. Gömülü tüm yazı tipleri yalnızca bu belgede kullanılan oymaları içerecek şekilde eksiltilir. Bu yazı tipi dosyalarının boyutunu küçültür. Kullanılmayan oymalara sahip büyük bir yazı tipi kullanıyorsanız faydalıdır.

--transform-css-rules

Bu kitaptaki CSS stillerini dönüştürmek için kurallar içeren bir dosyanın yolu. Böyle bir dosya oluşturmanın en kolay yolu, calibre arayüzündeki kural oluşturmak için kullanılan sihirbazı kullanmaktır. Sihirbaza dönüştürme iletişim kutusunun "Bak ve hisset->Stilleri dönüştür" bölümünden erişin. Kuralları oluşturduktan sonra, onları bir dosyaya kaydetmek için "Dışa Aktar" düğmesini kullanabilirsiniz.

--transform-html-rules

Bu kitaptaki HTML'yi dönüştürmek için kurallar içeren bir dosyanın yolu. Böyle bir dosya oluşturmanın en kolay yolu, calibre arayüzünde kurallar oluşturmak için sihirbazı kullanmaktır. Dönüştürme iletişim kutusunun "Bak & hisset->HTML Dönüştür" bölümünden erişin. Kuralları oluşturduktan sonra, onları bir dosyaya kaydetmek için "Dışa Aktar" düğmesini kullanabilirsiniz.

--unsmarten-punctuation

Süslü tırnak, tire ve üç nokta işaretlerini düz versiyonlarına çevir.

SEZGİSEL İŞLEME

Genel tanımlı motifleri kullanarak dökümanın yazılarını ve yapısını değiştirin. Default olarak pasif durumdadır. Aktifleştirmek için –enable-heuristics kullanın. Komutları tek tek pasifleştirmek için –disable-* ayarını kullanabilirsiniz.

--disable-dehyphenate

Belgedeki tirelenmiş kelimeleri analiz et. Belge tirelerin kalması veya gitmesi gerekliliğini belirlemek için sözlük olarak kullanılır.

--disable-delete-blank-paragraphs

Eğer tüm paragraflar arasında boş paragraflar varsa dökümandan bunları çıkar

--disable-fix-indents

Birden çok kesintisiz boşluk girdilerinden oluşan girintiyi CSS girintilerine çevir.

--disable-format-scene-breaks

Sola dayalı sahne bitiş işaretçileri ortalanır. Birden çok boş satır kullanan yumuşak sahne kesicileri yatay kurallarla değiştir.

--disable-italicize-common-cases

İtalik olacak ortak kelimelere ve modellere bak, ve bunları italikleştir.

--disable-markup-chapter-headings

Biçimlendirilmemiş bölüm başlıklarını ve altbaşlıklarını algıla. Onları h2 ve h3 etiketlerine çevir. Bu ayar içindekiler yaratmayacak ama oluşturulurken yapı algılama ile bağlantılı kullanılabilecek.

--disable-renumber-headings

Ardarda gelen <h1> veya <h2> etiketleri olup olmadığına bakar. Etiketler bölüm başlıklarının ortasında parçalanmanın önüne geçmek için yeniden numaralandırılır.

--disable-unwrap-lines

Noktalama işaretleri ve diğer biçim işaretlerini kullanırken satırları bölme

--enable-heuristics

Heuristic işlemeyi etkinleştir. Bu seçenek gerçekleşecek herhangi bir heurostic işleme için ayarlanmalıdır.

--html-unwrap-factor

Bir satırın erişmesi gereken uzunluğa karar verme ölçeği. Geçerli değerler 0 ila 1 arası ondalık sayılardır. Varsayılan değer 0.4 olup, ortalama satır uzunluğunun biraz altındadır. Eğer bir dökümanda bir iki satır sarkıyorsa, bu değer düşürülmelidir.

--replace-scene-breaks

Sahne kesicileri belirtilen metinle değiştir. Varsayılan olarak, girdi belgesindeki metin kullanılır.

BUL ve DEĞİŞTİR

Kullanıcı tanımlı motifleri kullanarak dökümanın yazılarını ve yapısını değiştir.

--search-replace

Path to a file containing search and replace regular expressions. The file must contain alternating lines of regular expression followed by replacement pattern (which can be an empty line). The regular expression must be in the Python regex syntax and the file must be UTF-8 encoded.

--sr1-replace

sr1-search aramasıyle bulunan metin için yeni değer

--sr1-search

sr1-replace araması ile değiştirmek için arama şablonu (regular expression- regex).

--sr2-replace

sr2-search araması ile bulunan metin için yeni değer.

--sr2-search

sr2-replace araması ile değiştirmek için arama şablonu (regular expression- regex).

--sr3-replace

sr3-search aramasıyle bulunan metin için yeni değer.

--sr3-search

sr3-replace araması ile değiştirmek için arama şablonu (regular expression- regex).

YAPI ALGILAMA
dosya yapısının otomatik farkınıa varma kontrolü

--chapter

An XPath expression to detect chapter titles. The default is to consider <h1> or <h2> tags that contain the words "chapter", "book", "section", "prologue", "epilogue" or "part" as chapter titles as well as any tags that have class="chapter". The expression used must evaluate to a list of elements. To disable chapter detection, use the expression "/". See the XPath Tutorial in the calibre User Manual for further help on using this feature.

--chapter-mark

Tesbit edilen bölümlerin nasıl işaretleneceğini belirleyin. "pagebreak" değeri bölümlerden önce sayfa sonu ekler. "rule" değeri bölümlerden önce bir çizgi ekler. "none" değeri bölüm işaretlemeyi pasifleştirir ve "both" değeri ise bölümlerden önce hem çizgi hem sayfa sonu ekler.

--disable-remove-fake-margins

Bazı dökümanlar sayfa kenar boşluklarını (margin) her bir paragraf için sağ ve sol boşlukları belirleyerek ayarlar. Calibre bu ayarları bulup iptal etmeye çalışır. Bu bazen kaldırılmaması gereken boşluk ayarlarının da kaldırılmasına neden olur. Böyle bir durumda bu boşlukların kaldırılması işlemini iptal edebilirsiniz.

--insert-metadata

Insert the book metadata at the start of the book. This is useful if your e-book reader does not support displaying/searching metadata directly.

--page-breaks-before

Bir XPath ifadesi. Sayfa kesmeleri belirtilen ögelerden önce yerleştirilir. İfadenin kullanımını kapatmak için bunu kullanın: /

--prefer-metadata-cover

Belirtilen kapak yerine kaynak dosyasındaki algılanan kapağı kullan

--remove-first-image

Remove the first image from the input e-book. Useful if the input document has a cover image that is not identified as a cover. In this case, if you set a cover in calibre, the output document will end up with two cover images if you do not specify this option.

--start-reading-at

An XPath expression to detect the location in the document at which to start reading. Some e-book reading programs (most prominently the Kindle) use this location as the position at which to open the book. See the XPath tutorial in the calibre User Manual for further help using this feature.

İÇİNDEKİLER

İçindekiler(TOC)’un otomatik oluşturulma ayarını düzenleyin. Eğer kaynak içindekiler(TOC)’e sahipse varsayılan olarak o kullanılacaktır.

--duplicate-links-in-toc

Girdideki linklerden içindekiler(TOC) oluştururken maddelerin birden fazla kopyasına izin ver. Yani farklı yerlere işaret etmesi şartıyla aynı yazıya sahip birden fazla maddeye izin ver.

--level1-toc

Birinci seviyede İçindekilere eklenmesi gereken tüm etiketleri belirten XPath ifadesi. Eğer bu belirtilirse, diğer otomatik algılama biçimleri üzerinde öncelik alır. Örnekler için Calibre Kullanıcı Kılavuzundaki XPath Rehberine göz atın.

--level2-toc

İkinci seviyede İçindekilere eklenmesi gereken tüm etiketleri belirten XPath ifadesi. Her girdi önceki birinci seviye girdinin altına eklenir. Örnekler için Calibre Kullanıcı Kılavuzundaki XPath Rehberine göz atın.

--level3-toc

Üçüncü seviyede İçindekilere eklenmesi gereken tüm etiketleri belirten XPath ifadesi. Her girdi önceki ikinci seviye girdinin altına eklenir. Örnekler için Calibre Kullanıcı Kılavuzundaki XPath Rehberine göz atın.

--max-toc-links

İçindekilere eklenecek maksimum link sayısı. Devre dışı bırakmak için 0'a ayarlayın. Varsayılan 50. Eğer eşik sayısından az bölüm algılanırsa linkler sadece içindekilere eklenir

--no-chapters-in-toc

Otomatik algılanmış bölümleri İçindekiler kısmına ekleme

--toc-filter

Belirtilen kurallı ifadeyle eşleşen İçindekiler başlıklarını, İçindekilerden çıkarır. Eşleşen girdiler ve onların alt girdilerinin tümü çıkartılır.

--toc-threshold

Eğer bu sayıdan daha az bölüm algılanırsa linkler İçindekiler kısmına eklenir. Varsayılan: 6

--use-auto-toc

Normalde kaynak içindekiler bilgisine sahipse otomatik üretilene tercihen bu bilgi kullanılır. Bu seçenek ile her zaman otomatik üretilen kullanılır

ÜSTBİLGİ

Çıkış almak için metadata seçeneklerini kaydet.

--author-sort

Yazar'a göre sıralamada kullanılacak metin

--authors

Yazarları ayarla. Birden fazla yazar varsa ampersand (&) işareti ile ayrılmalı.

--book-producer

Kitap üreticisini ata.

--comments

Ekitap açıklamasını ayarla.

--cover

Kapağı belirli bir dosya veya URL'ye ayarla

--isbn

Kitabın ISBN'ini ayarla.

--language

Dili ayarla.

--pubdate

Set the publication date (assumed to be in the local timezone, unless the timezone is explicitly specified)

--publisher

Ekitap yayıncısını ayarla.

--rating

Derecelendirmesini ayarla. 1 ve 5 arasında bir sayı olmalı.

--read-metadata-from-opf, --from-opf, -m

Metaveriyi seçili OPF dosyasından oku. Bu dosyadan okunacak metaveri, kaynak dosyadaki herhangi bir metaverinin yerine geçecektir.

--series

Ekitabın bağlı olduğu seriyi ayarla.

--series-index

Kitabın serideki indeksini ayarla.

--tags

Kitap için etiketleri ayarla. Virgül ile ayrılmış liste olmalı.

--timestamp

Kitap zamandamgasını tanımla(Artık kullanılmıyor)

--title

Başlığı ayarla.

--title-sort

Başlık sürümü sıralama için kullanılır.

HATA AYIKLA

Dönüştürme hatalarını bulmak için yardım seçenekleri

--debug-pipeline, -d

Dönüşüm hattının farklı aşamalarındaki çıktıyı belirtilen klasöre kaydedin. Dönüştürme sürecinin hangi aşamasında bir hatanın oluştuğundan emin değilseniz kullanışlıdır.

--verbose, -v

Ayrıntı seviyesi. Daha fazla ayrıntı için birden fazla belirtin. İki kere belirtmek tam ayrıntıya sebep olur, bir kere belirtmen orta seviye ayrıntı ve hiç belirtmemek en az ayrıntıya.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- ebook-edit

ebook-edit

ebook-edit [opts] [path_to_ebook] [name_of_file_inside_book ...]

Launch the calibre Edit book tool. You can optionally also specify the names of files inside the book which will be opened for editing automatically.

Whenever you pass arguments to ebook-edit that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--detach

Detach from the controlling terminal, if any (Linux only)

--help, -h

bu yardım iletisini göster ve çık

--select-text

The text to select in the book when it is opened for editing

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- ebook-meta

ebook-meta

ebook-meta ebook_file [seçenekler]

Read/Write metadata from/to e-book files.

Supported formats for reading metadata: azw, azw1, azw3, azw4, cb7, cbc, cbr, cbz, chm, docx, epub, fb2, fbz, html, htmlz, imp, lit, lrf, lrx, mobi, odt, oebzip, opf, pdb, pdf, pml, pmlz, pobi, prc, rar, rb, rtf, snb, tpz, txt, txtz, updb, zip

Supported formats for writing metadata: azw, azw1, azw3, azw4, docx, epub, fb2, fbz, htmlz, lrf, mobi, odt, pdb, pdf, prc, rtf, tpz, txtz

Different file types support different kinds of metadata. If you try to set some metadata on a file type that does not support it, the metadata will be silently ignored.

Whenever you pass arguments to ebook-meta that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--author-sort

Yazarlar sıralanırken kullanılacak karakter dizisi. Belirtilmemişse ama yazar(lar) mevcutsa, otomatik olarak başlıktan çıkartılır.

--authors, -a

Yazarları ayarla. Birden fazla yazar birbirinden & karakteri ile ayrılmalı. Yazar isimleri önce isim sonra soyad düzeninde olmalı

--book-producer, -k

Kitap üreticisini ata.

--category

Kitap kategorisini ayarla.

--comments, -c

Ekitap açıklamasını ayarla.

--cover

Belirlenen dosyaya kapak ayarlar.

--date, -d

Yayın tarihini ayarla.

--from-opf

Read metadata from the specified OPF file and use it to set metadata in the e-book. Metadata specified on the command line will override metadata read from the OPF file

--get-cover

Get the cover from the e-book and save it at as the specified file.

--help, -h

bu yardım iletisini göster ve çık

--identifier

Set the identifiers for the book, can be specified multiple times. For example: --identifier uri:https://acme.com --identifier isbn:12345 To remove an identifier, specify no value, --identifier isbn: Note that for EPUB files, an identifier marked as the package identifier cannot be removed.

--index, -i

Kitabın serideki indeksini ayarla.

--isbn

Kitabın ISBN'ini ayarla.

--language, -l

Dili ayarla.

--lrf-bookid

LRF dosyalarına (Kitap Kimliği) koy

--publisher, -p

Ekitap yayıncısını ayarla.

--rating, -r

Derecelendirmesini ayarla. 1 ve 5 arasında bir sayı olmalı.

--series, -s

Ekitabın bağlı olduğu seriyi ayarla.

--tags

Kitap için etiketleri ayarla. Virgül ile ayrılmış liste olmalı.

--title, -t

Başlığı ayarla.

--title-sort

Sıralama için kullanılan başlığın sürümü. Belirtilmemişse ama başlık mevcutsa, otomatik olarak başlıktan çıkartılır.

--to-opf

OPF dosyasının ismini belirle. Bu metadata OPF dosyasına yazılacak.

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- ebook-polish

ebook-polish

ebook-polish [seçenekler] input_file [çıktı_dosyası]

Polishing books is all about putting the shine of perfection onto your carefully crafted e-books.

Polishing tries to minimize the changes to the internal code of your e-book. Unlike conversion, it does not flatten CSS, rename files, change font sizes, adjust margins, etc. Every action performs only the minimum set of changes needed for the desired effect.

You should use this tool as the last step in your e-book creation process.

Note that polishing only works on files in the AZW3 veya EPUB formats.

Whenever you pass arguments to ebook-polish that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--add-soft-hyphens, -H

Add soft hyphens to all words in the book. This allows the book to be rendered better when the text is justified, in readers that do not support hyphenation.

--compress-images, -i

Kitaptaki resimleri, dosya boyutunu azaltmak için ve resim kalitesini etkilemeden kayıpsız olarak sıkıştır.

--cover, -c

Path to a cover image. Changes the cover specified in the e-book. If no cover is present, or the cover is not properly identified, inserts a new cover.

--embed-fonts, -e

Belgede başvurulan ve hala gömülü olmayan tüm yazı tiplerini göm. Bu işlem bilgisayarınızı yazı tipleri için tarar ve bulunurlarsa, belgeye gömülürler. Lütfen bu belgede kullanılan yazı tipleri için gerekli lisansa sahip olduğunuzdan emin olun.

--help, -h

bu yardım iletisini göster ve çık

--jacket, -j

Kitabın başına başlık, etiketler, yazarlar, seriler, yorumlar, vs. gibi tüm kitap metadata'sını içeren bir "kitap ceketi" ekle. Önceki herhangi kitap ceketi değiştirilir.

--opf, -o

Bir OPF dosyasının yolu. Kitabın üstbilgisi OPF dosyasından güncellenir.

--remove-jacket

Önceden eklenmiş bir kitap ceketi sayfasını sil.

--remove-soft-hyphens

Remove soft hyphens from all text in the book.

--remove-unused-css, -u

Remove all unused CSS rules from stylesheets and <style> tags. Some books created from production templates can have a large number of extra CSS rules that don't match any actual content. These extra rules can slow down readers that need to parse them all.

--smarten-punctuation, -p

Düz metin tire, elipse, tırnak, çift tire, vs. gibi karakterleri tipografik olarak doğru eşitlerine çevir. Algoritmanın bazen yanlış sonuçlar üretebildiğine dikkat edin, özellikle küçülmelerin başında tek tırnak olduğunda.

--subset-fonts, -f

Yazı tiplerinin alt kümelenmesi gömülü bir yazı tipinin kitapta yalnızca bu yazı tipinden karakterleri içerecek hale indirgenmesi demektir. Bu yazı tipi dosyalarının boyutlarını oldukça azaltır (yarıya düşürmesine çokça rastlanır). Örneğin, kitap başlıklar için özel bir yazı tipi kullanıyorsa, alt kümeleme bu yazı tipinin yalnızca kitaptaki asıl başlıklarda kullanılan karakterleri kullanacak şekilde indirgenmesini sağlar. Veya gömülü yazı tipi bir yazı tipinin kalın ve yatık sürümlerini gömüyorsa, ama kalın ve yatık metne çok az rastlanıyorsa veya yoklarsa, kalın ve yatık yazı tipleri yalnızca birkaç karaktere indirgenebilir veya tamamen kaldırılabilir. Alt kümelemenin tek eksi tarafı, ileri bir tarihte kitaplara daha fazla metin koymaya karar verdiğinizde, yeni eklenen metnin alt kümelenmiş yazı tipi tarafından kapsanmayabileceğidir.

--upgrade-book, -U

Upgrade the internal structures of the book, if possible. For instance, upgrades EPUB 2 books to EPUB 3 books.

--verbose

Daha ayrıntılı çıktı üret, hata ayıklama için faydalıdır.

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- ebook-viewer

ebook-viewer

ebook-viewer [options] file

View an e-book.

Whenever you pass arguments to ebook-viewer that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--continue

Continue reading the last opened book

--detach

Detach from the controlling terminal, if any (Linux only)

--force-reload

Force reload of all opened books

--full-screen, --fullscreen, -f

If specified, the E-book viewer window will try to open full screen when started.

--help, -h

bu yardım iletisini göster ve çık

--new-instance

Open a new viewer window even when the option to use only a single viewer window is set

--open-at

The position at which to open the specified book. The position is a location or position you can get by using the Go to->Location action in the viewer controls. Alternately, you can use the form toc:something and it will open at the location of the first Table of Contents entry that contains the string "something". The form toc-href:something will match the href (internal link destination) of toc nodes. The matching is exact. If you want to match a substring, use the form toc-href-contains:something. The form ref:something will use Reference mode references. The form search:something will search for something after opening the book. The form regex:something will search for the regular expression something after opening the book.

--raise-window

If specified, the E-book viewer window will try to come to the front when started.

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- fetch-ebook-metadata

fetch-ebook-metadata

fetch-ebook-metadata [seçenekler]

Kitap üstbilgisini çevrim içi kaynaklardan getir. Yazar, Kitap Adı veya ISBN bilgilerinden en az birini belirlemelisiniz.

Whenever you pass arguments to fetch-ebook-metadata that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--allowed-plugin, -p

Specify the name of a metadata download plugin to use. By default, all metadata plugins will be used. Can be specified multiple times for multiple plugins. All plugin names: Google, Google Images, Amazon.com, Edelweiss, Open Library, Big Book Search

--authors, -a

Kitap yazar(lar)ı

--cover, -c

Bir dosya adı belirtin. Kapak, eğer varsa, onun içine kaydedilecektir. Bu seçenek olmadan kapak indirilmeyecektir.

--help, -h

bu yardım iletisini göster ve çık

--identifier, -I

Identifiers such as ASIN/Goodreads id etc. Can be specified multiple times for multiple identifiers. For example: --identifier asin:B0082BAJA0

--isbn, -i

ISBN Kodu

--opf, -o

Üstbilgiyi okuyabilir metin yerine OPF biçiminde çıktı ver.

--timeout, -d

Saniye cinsinden zaman. Varsayılan 30'dur.

--title, -t

Kitap başlığı

--verbose, -v

Sistem günlüğünü konsola yazdır (stderr)

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- lrf2lrs

lrf2lrs

lrf2lrs book.lrf

LRF dosyasını LRS’ye çevir (XML UTF-8 kodlama) dosyası

Whenever you pass arguments to lrf2lrs that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--dont-output-resources

Gömülmüş resimleri ve font dosyalarını diske kaydetme

--help, -h

bu yardım iletisini göster ve çık

--output, -o

LRS dosyasını dışa aktar

--verbose

Daha çok ayrıntı ver

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- lrfviewer

lrfviewer

lrfviewer [options] book.lrf

Read the LRF e-book book.lrf

Whenever you pass arguments to lrfviewer that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--disable-hyphenation

Hecelemeyi kapat. Gerçeklemeyi önemli ölçüde hızlandıracaktır.

--help, -h

bu yardım iletisini göster ve çık

--profile

LRF oluşturucu profili

--verbose

Gerçekleme süreci hakkında daha fazla bilgi yazdır

--version

programın sürüm numarasını göster ve çık

--visual-debug

Gerçekleme motorunun hatalarını ayıklamak için görsel yardımları aç

--white-background

Gözler için daha kolay olduğunu düşündüğümden varsayılan olarak arkaplan kapalıdır. Bu seçeneği arkaplanı tamamen beyaz yapmak için kullanın.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- lrs2lrf

lrs2lrf

lrs2lrf [seçenekler] file.lrs

Bir LRS dosyasından LRF dosya derle.

Whenever you pass arguments to lrs2lrf that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--help, -h

bu yardım iletisini göster ve çık

--lrs

LRS dosyasından LRS dosyası derle, hata ayıklama için faydalıdır.

--output, -o

Çıktı dosyasının yolu

--verbose

Ayrıntılı işleme

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Komut Satırı Arayüzü »
- web2disk

web2disk

web2disk URL

Where URL is for example https://google.com

Whenever you pass arguments to web2disk that have spaces in them, enclose the arguments in quotation marks. For example: “/some path/with spaces”

[seçenekler]

--base-dir, -d

Base folder into which URL is saved. Default is .

--delay

Ardışık eşleştirmeler arasındaki en kısa zaman aralığı. Varsayılan 0 saniyedir

--dont-download-stylesheets

CSS stil yüklemeyin

--encoding

İndirmeye çalıştığınız web siteleri için karakter kodlaması. Varsayılan karakter kodlamasının tahmin edilmeye çalışılmasıdır.

--filter-regexp

Bu düzenli ifadeyle eşleşen bağlantı göz ardı edilir. Bu seçenek birden fazla kez belirtilebilir, bu durumda herhangi bir düzenli ifade bağlantıyla eşleşirse, göz ardı edilir. Varsayılan olarak, hiçbir bağlantı göz ardı edilmez. Eğer hem filtre düzenli ifadesi, hem eşleşme düzenli ifadesi belirtilmişse, filtre düzenli ifadesi önce uygulanır.

--help, -h

bu yardım iletisini göster ve çık

--match-regexp

Yalnızca bu düzenli ifade ile eşleşen bağlantılar takip edilir. Bu seçenek birden fazla kez belirtilebilir, bu durumda bağlantı düzenli ifadelerden biriyle eşleştiği sürece, takip edilecektir. Varsayılan olarak tüm bağlantılar takip edilir.

--max-files, -n

İndirilecek maksimum dosya sayısı. Bu sadece <a href> etiketlerindeki dosyalara uygulanır. Öntanımlısı 9223372036854775807

--max-recursions, -r

İç içe takip edilecek bağlantı sayısı. Varsayılan 1

--timeout, -t

Sunucu cevabı için, saniye olarak zaman aşımı bekleme süresi . Varsayılan : 10.0 sn

--verbose

Ayrıntılı çıktı bilgisi göster. Hata ayıklamada faydalıdır

--version

programın sürüm numarasını göster ve çık

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Calibre geliştirme ortamı ayarlamak

Calibre geliştirme ortamı ayarlamak
calibre is completely open source, licensed under the GNU GPL v3 [https://www.gnu.org/licenses/gpl.html]. This means that you are free to download and modify the program to your heart’s content. In this section, you will learn how to get a calibre development environment set up on the operating system of your choice. calibre is written primarily in Python [https://www.python.org] with some C/C++ code for speed and system interfacing. Note that calibre requires at least Python 3.8.

İçindekiler

- Tasarım felsefesi

- Kod düzeni

- Kodun alınması

- Değişikliklerinizin içerilmesi için gönderilmesi

- Windows geliştirme ortamı

- macOS development environment

- Linux geliştirme ortamı

- Aynı bilgisayarda ayrı “normal” ve “geliştirme” calibre yüklemelerini bulundurmak

- Hata ayıklama ipuçları

- Print ifadeleri kullanarak

- Using an interactive Python interpreter

- Using the Python debugger as a remote debugger

- Using the debugger in your favorite Python IDE

- Executing arbitrary scripts in the calibre Python environment

- Projelerinizde calibre kullanmak

- Calibre’nin ikilik kurulumu

- Linux üstünde kaynak kurulum

- Calibre’nin çeşitli kısımları için API belgelendirmesi

Tasarım felsefesi
calibre’nin kökleri Unix dünyasına uzanır, yani tasarımı oldukça modülerdir. Modüller birbirleriyle iyi tanımlanmış arayüzlerle iletişim kurarlar. Bu da calibre’ye yeni özellikler ekleme ve hata ayıklamayı çok kolay kılarak hızlı geliştirme temposu oluşturur. Kökleri sebebiyle, calibre’nin tüm fonksiyonları için Komut Satırı Arayüzü de belgelenmiş kapsamlı bir komut satırı arayüzü vardır.

The modular design of calibre is expressed via Plugins. There is a tutorial on writing calibre plugins. For example, adding support for a new device to calibre typically involves writing less than a 100 lines of code in the form of a device driver plugin. You can browse the built-in drivers [https://github.com/kovidgoyal/calibre/tree/master/src/calibre/devices]. Similarly, adding support for new conversion formats involves writing input/output format plugins. Another example of the modular design is the recipe system for fetching news. For more examples of plugins designed to add features to calibre, see the Index of plugins [https://www.mobileread.com/forums/showthread.php?p=1362767#post1362767].

Kod düzeni

All the calibre Python code is in the calibre package. This package contains the following main sub-packages

- devices - Tüm aygıt sürücüleri. Bazı yerleşik sürücülere bakmanız nasıl çalıştıkları konusunda fikir edinmeniz için yeterli olacaktır.

- Detaylar için: aygıt sürücüleri tarafından desteklenen arayüzü tanımlayan devices.interface ve bir USBMS aygıtına bağlanan genel bir sürücü tanımlayan devices.usbms e göz atın. Calibre’deki tüm USBMS temelli sürücüler ondan türemiştir.

- e-books - All the e-book conversion/metadata code. A good starting point is calibre.ebooks.conversion.cli which is the module powering the ebook-convert command. The conversion process is controlled via conversion.plumber. The format independent code is all in ebooks.oeb and the format dependent code is in ebooks.format_name.

- Metadata okuma, yazma ve indirme tamamen ebooks.metadata içindedir

- Conversion happens in a pipeline, for the structure of the pipeline, see Giriş. The pipeline consists of an input plugin, various transforms and an output plugin. The code that constructs and drives the pipeline is in plumber.py. The pipeline works on a representation of an e-book that is like an unzipped epub, with manifest, spine, toc, guide, html content, etc. The class that manages this representation is OEBBook in ebooks.oeb.base. The various transformations that are applied to the book during conversions live in oeb/transforms/*.py. And the input and output plugins live in conversion/plugins/*.py.

- E-book editing happens using a different container object. It is documented in E-kitap düzenleme araçları için API belgeleri.

- db - Veri tabanı arka ucu. Calibre kitaplığına arayüz için Veri tabanı arayüzü için API belgelendirmesi ye göz atın.

- Content server: srv is the calibre Content server.

- gui2 - The Graphical User Interface. GUI initialization happens in gui2.main and gui2.ui. The e-book-viewer is in gui2.viewer. The e-book editor is in gui2.tweak_book.

Çeşitli calibre çalıştırılabilirleri için giriş noktalarını bulmak isterseniz, linux.py [https://github.com/kovidgoyal/calibre/blob/master/src/calibre/linux.py] içindeki ``entry_points``e bakın.

If you need help understanding the code, post in the development forum [https://www.mobileread.com/forums/forumdisplay.php?f=240] and you will most likely get help from one of calibre’s many developers.

Kodun alınması

You can get the calibre source code in two ways, using a version control system or directly downloading a tarball [https://calibre-ebook.com/dist/src].

calibre uses Git [https://www.git-scm.com/], a distributed version control system. Git is available on all the platforms calibre supports. After installing Git, you can get the calibre source code with the command:

git clone https://github.com/kovidgoyal/calibre.git

Windows üstünde tam yol ismine ihtiyacınız olacaktır, şuna benzer bir şey olmalı C:\Program Files\Git\git.exe.

calibre çok geniş ve çok uzun kaynak kontrol geçmişine sahip bir projedir, yani yukarıdaki işlem bir miktar sürebilir (internet hızınıza bağlı olarak 10 dakikadan bir saate kadar).

If you want to get the code faster, the source code for the latest release is always available as an archive [https://calibre-ebook.com/dist/src].

Bir dalı son koda güncellemek için, şu komutu kullanın:

git pull --no-edit

You can also browse the code at GitHub [https://github.com/kovidgoyal/calibre].

Değişikliklerinizin içerilmesi için gönderilmesi

Yalnızca bir kaç küçük değişiklik yapmak istiyorsanız, değişikliklerinizi yapıp calibre hata takibinde [https://bugs.launchpad.net/calibre] bir bilete ekleyebileceğiniz bir “merge talimatı” oluşturun. Bu işlem için değişikliklerinizi yapıp şunu çalıştırın:

git commit -am "Comment describing your changes"
git format-patch origin/master --stdout > my-changes

This will create a my-changes file in the current folder, simply attach that to a ticket on the calibre bug tracker [https://bugs.launchpad.net/calibre]. Note that this will include all the commits you have made. If you only want to send some commits, you have to change origin/master above. To send only the last commit, use:

git format-patch HEAD~1 --stdout > my-changes

Son n gönderiyi yollamak için, 1 i n ile değiştirin, örneğin, son 3 gönderi için:

git format-patch HEAD~3 --stdout > my-changes

Be careful to not include merges when using HEAD~n.

If you plan to do a lot of development on calibre, then the best method is to create a GitHub [https://github.com] account. Below is a basic guide to setting up your own fork of calibre in a way that will allow you to submit pull requests for inclusion into the main calibre repository:

- Makalede gösterildiği gibi makinenize git kurun: Git Kurulumu [https://help.github.com/articles/set-up-git]

- Şurada anlatıldığı gibi, GitHub’a kimlik doğrulama için ssh anahtarları kurun: SSH anahtarları üretme [https://help.github.com/articles/generating-ssh-keys]

- https://github.com/kovidgoyal/calibre adresine gidin ve Fork düğmesine tıklayın.

- Bir Terminal açın ve:

git clone git@github.com:<username>/calibre.git
git remote add upstream https://github.com/kovidgoyal/calibre.git

Replace <username> above with your GitHub username. That will get your fork checked out locally.

- İstediğiniz gibi değişiklikler yapabilir ve istediğiniz zaman gönderebilirsiniz. Yaptığınız işler birleştirmeye hazır olduğunda:

git push

sonra da https://github.com/<username>/calibre adresine gidin ve Pull Request düğmesine tıklayıp birleştirme işlemi için bir çekme isteği üretin.

- Herhangi bir anda yerel kopyanızı ana depo ile şu şekilde güncel tutabilirsiniz:

git pull upstream

You should also keep an eye on the calibre development forum [https://www.mobileread.com/forums/forumdisplay.php?f=240]. Before making major changes, you should discuss them in the forum or contact Kovid directly (his email address is all over the source code).

Windows geliştirme ortamı

Not

Yukarda anlatıldığı gibi calibre kaynak kodunu almalısınız.

Install calibre normally, using the Windows installer [https://calibre-ebook.com/download_windows]. Then open a Command Prompt and change to the previously checked out calibre code folder. For example:

cd C:\Users\kovid\work\calibre

calibre is the folder that contains the src and resources sub-folders.

The next step is to set the environment variable CALIBRE_DEVELOP_FROM to the absolute path of the src folder. So, following the example above, it would be C:\Users\kovid\work\calibre\src. Here is a short guide [https://docs.python.org/using/windows.html#excursus-setting-environment-variables] to setting environment variables on Windows.

Çevre değişkenini ayarladıktan sonra, yeni bir komut satırı açın ve şu komutu kullanarak düzgün ayarlandığını doğrulayın:

echo %CALIBRE_DEVELOP_FROM%

Bu çevre değişkenini ayarlamak calibre’nin belirtilen konumdan tüm Python kodunu yükleyeceği anlamına gelir.

Bu kadar! Şimdi calibre kodu üstünde çalışmaya başlayabilirsiniz. Örneğin favori düzenleyicinizle src\calibre\__init__.py dosyasını açın ve şu satırı ekleyin:

print("Hello, world!")

dosyanın üst kısmına doğru. Şimdi calibredb komutunu çalıştırın. Çıktının ilk satırı Hello, world! olmalı.

You can also setup a calibre development environment inside the free Microsoft Visual Studio, if you like, following the instructions here [https://www.mobileread.com/forums/showthread.php?t=251201].

macOS development environment

Not

Yukarda anlatıldığı gibi calibre kaynak kodunu almalısınız.

Install calibre normally using the provided .dmg [https://calibre-ebook.com/download_osx]. Then open a Terminal and change to the previously checked out calibre code folder, for example:

cd /Users/kovid/work/calibre

calibre is the folder that contains the src and resources sub-folders. The calibre command line tools are found inside the calibre app bundle, in /Applications/calibre.app/Contents/MacOS you should add this folder to your PATH environment variable, if you want to run the command line tools easily.

The next step is to create a bash script that will set the environment variable CALIBRE_DEVELOP_FROM to the absolute path of the src folder when running calibre in debug mode.

Bir düz metin dosyası oluşturun:

#!/bin/sh
export CALIBRE_DEVELOP_FROM="/Users/kovid/work/calibre/src"
calibre-debug -g

Save this file as /usr/local/bin/calibre-develop, then set its permissions so that it can be executed:

chmod +x /usr/local/bin/calibre-develop

Bu işlemden sonra şunu çalıştırın:

calibre-develop

Calibre başlarken Uç birim penceresinde bazı tanılama bilgileri görüyor olmalısınız, ve GUI penceresinde sürüm numarasından sonra kaynaktan çalıştırdığınızı belirten bir yıldız işareti görmelisiniz.

Linux geliştirme ortamı

Not

Yukarda anlatıldığı gibi calibre kaynak kodunu almalısınız.

calibre is primarily developed on Linux. You have two choices in setting up the development environment. You can install the calibre binary as normal and use that as a runtime environment to do your development. This approach is similar to that used in Windows and macOS. Alternatively, you can install calibre from source. Instructions for setting up a development environment from source are in the INSTALL file in the source tree. Here we will address using the binary as a runtime, which is the recommended method.

Install calibre using the binary installer [https://calibre-ebook.com/download_linux]. Then open a terminal and change to the previously checked out calibre code folder, for example:

cd /home/kovid/work/calibre

calibre is the folder that contains the src and resources sub-folders.

The next step is to set the environment variable CALIBRE_DEVELOP_FROM to the absolute path of the src folder. So, following the example above, it would be /home/kovid/work/calibre/src. How to set environment variables depends on your Linux distribution and what shell you are using.

Not

It is recommended to use the binary installer provided from upstream. Should you insist on using a package provided by your distribution, use the CALIBRE_PYTHON_PATH and CALIBRE_RESOURCES_PATH variables instead.

Çevre değişkenini ayarladıktan sonra, bir uç birim açın şu komutu kullanarak düzgün ayarlanıp ayarlanmadığını kontrol edin:

echo $CALIBRE_DEVELOP_FROM

Bu çevre değişkenini ayarlamak calibre’nin belirtilen konumdan tüm Python kodunu yükleyeceği anlamına gelir.

Bu kadar! Şimdi calibre kodu üstünde çalışmaya başlayabilirsiniz. Örneğin favori düzenleyicinizle src/calibre/__init__.py dosyasını açın ve şu satırı ekleyin:

print("Hello, world!")

dosyanın üst kısmına doğru. Şimdi calibredb komutunu çalıştırın. Çıktının ilk satırı Hello, world! olmalı.

Aynı bilgisayarda ayrı “normal” ve “geliştirme” calibre yüklemelerini bulundurmak

Calibre kaynak ağacı oldukça kararlıdır ve çok nadiren kırılır, ama ayrı bir test kitaplığından kaynaktan çalıştırma ve yayınlanan calibre sürümünü gündelik kitaplığınızla çalıştırma gibi bir ihtiyaç duyarsanız, .bat dosyaları veya kabuk betikleri kullanarak calibre’yi başlatabilirsiniz. Aşağıdaki örnek Windows üzerinde .bat dosyaları (diğer platformlar için de yönergeler aynıdır, .bat dosyası yerine kabuk betiği kullanmanız yeterli) ile bu işin nasıl yapılacağını gösteriyor

Günlük kitaplığınızla calibre’nin yayınlanan sürümünü başlatmak için:

calibre-normal.bat:

calibre.exe "--with-library=C:\path\to\everyday\library folder"

calibre-dev.bat:

set CALIBRE_DEVELOP_FROM=C:\path\to\calibre\checkout\src
calibre.exe "--with-library=C:\path\to\test\library folder"

Hata ayıklama ipuçları

Python iç gözlem için mükemmel imkanları olan dinamik yazılan bir dildir. Kovid calibre çekirdek kodunu hata ayıklayıcı kullanmadan yazdı. Calibre kodunda hata ayıklama için bir çok taktik vardır:

Print ifadeleri kullanarak

Kovid’in favori hata ayıklama yöntemi. İlgilendiğiniz noktalara print ifadeleri koyun ve programı uç birimde çalıştırın. Örneğin GUI’yi uç birimden şu şekilde başlatabilirsiniz:

calibre-debug -g

Similarly, you can start the E-book viewer as:

calibre-debug -w /path/to/file/to/be/viewed

The e-book editor can be started as:

calibre-debug --edit-book /path/to/be/edited

Using an interactive Python interpreter

You can insert the following two lines of code to start an interactive Python session at that point:

from calibre import ipython
ipython(locals())

When running from the command line, this will start an interactive Python interpreter with access to all locally defined variables (variables in the local scope). The interactive prompt even has Tab completion for object properties and you can use the various Python facilities for introspection, such as dir(), type(), repr(), etc.

Using the Python debugger as a remote debugger

You can use the builtin Python debugger (pdb) as a remote debugger from the command line. First, start the remote debugger at the point in the calibre code you are interested in, like this:

from calibre.rpdb import set_trace
set_trace()

Ardından calibre’yi normal ya da önceki kısımda açıklanan calibre hata ayıklama komutlarından birini kullanarak çalıştırın. Kodda yukardaki noktaya erişildiğinde calibre donacak ve hata ayıklayıcının bağlanmasını bekleyecektir.

Şimdi bir uç birim açın ve hata ayıklama oturumunu başlatmak için aşağıdaki komutu kullanın:

calibre-debug -c "from calibre.rpdb import cli; cli()"

You can read about how to use the Python debugger in the Python stdlib docs for the pdb module [https://docs.python.org/library/pdb.html#debugger-commands].

Not

Varsayılan olarak, uzak hata ayıklayıcı port 4444’den bağlanmaya çalışacaktır. Yukardaki set_trace() ve cli() fonksiyonlarına port parametresini set_trace(port=1234) ve cli(port=1234) şeklinde geçirerek değiştirebilirsiniz.

Not

The Python debugger cannot handle multiple threads, so you have to call set_trace once per thread, each time with a different port number.

Using the debugger in your favorite Python IDE

It is possible to use the builtin debugger in your favorite Python IDE, if it supports remote debugging. The first step is to add the calibre src checkout to the PYTHONPATH in your IDE. In other words, the folder you set as CALIBRE_DEVELOP_FROM above, must also be in the PYTHONPATH of your IDE.

Then place the IDE’s remote debugger module into the src sub-folder of the calibre source code checkout. Add whatever code is needed to launch the remote debugger to calibre at the point of interest, for example in the main function. Then run calibre as normal. Your IDE should now be able to connect to the remote debugger running inside calibre.

Executing arbitrary scripts in the calibre Python environment
calibre-debug komutu calibre modüllerine erişimi olan kodunuzun çalışmasını sağlayan bir kaç pratik anahtar içerir:

calibre-debug -c "some Python code"

is great for testing a little snippet of code on the command line. It works in the same way as the -c switch to the Python interpreter:

calibre-debug myscript.py

kendi Python betiğinizi çalıştırmak için kullanılabilir. Betiğin Python yorumlayıcısına verilmesiyle aynı şekilde çalışır, tek fark calibre ortamının tamamen başlatılmış olması, yani betiğinizde tüm calibre kodunu kullanabilecek olmanızdır. Betiğinizle komut satırı bağımsız değişkenlerini kullanmak için şu biçimi kullanın:

calibre-debug myscript.py -- --option1 arg1

-- takip eden tüm değişkenlerin betiğinize geçirilmesine sebep olur.

Projelerinizde calibre kullanmak

Python projenizde calibre fonksiyonlarını/kodunu doğrudan kullanmak mümkündür. Bunu yapmanın iki yolu vardır:

Calibre’nin ikilik kurulumu

Eğer calibre’nin ikilik kurulumuna sahipseniz, calibre ile gelen Python yorumlayıcısını şu şekilde kullanabilirsiniz:

calibre-debug /path/to/your/python/script.py -- arguments to your script

Linux üstünde kaynak kurulum

Yukardaki yöntemin yanında, Linux üstünde kaynaktan kurulum yaparsanız, calibre’yi doğrudan şu şekilde içe aktarabilirsiniz:

import init_calibre
import calibre

print(calibre.__version__)

Calibre kodunu çalıştırmak için yorumlayıcıyı ayarladığından, herhangi başka calibre modülünden/paketinden önce init_calibre modülünü içe aktarmanız önemlidir.

Calibre’nin çeşitli kısımları için API belgelendirmesi

- API documentation for recipes

- Eklentiler için API belgeleri

- Veri tabanı arayüzü için API belgelendirmesi

- E-kitap düzenleme araçları için API belgeleri

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Calibre geliştirme ortamı ayarlamak »

- Veri tabanı arayüzü için API belgelendirmesi

Veri tabanı arayüzü için API belgelendirmesi

Bu API süreç güvenlidir (çoklu okuma, tekil yazmalı kitleme şeması kullanır). API’ye şu şekilde erişebilirsiniz:

from calibre.library import db
db = db('Path to calibre library folder').new_api

Eğer ana calibre GUI’sinin bir parçası olan bir calibre eklentisi içindeyseniz, erişimi şu şekilde sağlayabilirsiniz:

db = self.gui.current_db.new_api

class calibre.db.cache.Cache(backend, library_database_instance=None)[kaynak]

Calibre kitaplığındaki bir metadata.db dosyasının bellekteki zulası. Bu sınıf ayrıca veri tabanına erişmek için süreç güvenli bir API olarak hizmet verir. Bellekteki zula en iyi performans için normal biçiminde devam ettirilir.

SQLITE metadata.db’den kararlı şekilde okuma yazma yapma yöntemi olarak kullanılır. Tüm tablo okuma/sıralama/arama/zulalama mantığı tekrar uygulanmıştır. Bu işlem en iyi performans ve esneklik için gerekliydi.

class EventType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

book_created = 4

Veritabanında kitap kimliğinin tek argüman olduğu yeni bir kitap kaydı oluşturulduğunda

book_edited = 8

Bir kitap biçimi bağımsız değişkenlerle düzenlendiğinde: (book_id, fmt)

books_removed = 5

Kitaplar veritabanından tek argüman olarak kitap kimlikleri listesiyle kaldırıldığında

format_added = 2

Bir kitaba şu argümanlarla bir biçim eklendiğinde: (book_id, format)

formats_removed = 3

Biçimler bir kitaptan bağımsız değişkenlerle kaldırıldığında: (kitap kimliğinin kitaptan kaldırılan formatlar kümesiyle eşleştirilmesi)

indexing_progress_changed = 9

Dizin oluşturma ilerlemesi değiştiğinde

items_removed = 7

Bazı kitaplardan etiket veya yazar gibi öğeler kaldırıldığında. Bağımsız Değişkenler: (field_name, etkilenen kitap kimlikleri, kaldırılan öğelerin kimlikleri)

items_renamed = 6

Etiketler veya yazarlar gibi öğeler kitapların bazılarında veya tümünde yeniden adlandırıldığında. Bağımsız Değişkenler: (alan_adı, etkilenen kitap kimlikleri, eski öğe kimliğinin yeni öğe kimliğiyle eşleştirilmesi)

metadata_changed = 1

Bazı kitaplar için bazı meta veriler bağımsız değişkenlerle değiştirildiğinde: (değiştirilen alanın adı, etkilenen kitap kimlikleri kümesi)

add_books(books, add_duplicates=True, apply_import_tags=True, preserve_uuid=False, run_hooks=True, dbapi=None)[kaynak]

Belirtilen kitapları kitaplığa ekle. Kitaplar, mi bir Metadata nesnesi ve format_map {fmt: path_or_stream} biçiminde bir sözlük nesnesi olmak üzere her biri (mi, format_map) biçiminde olan 2-tuple’ın yinelenebiliri olmalıdır, örneğin: {'EPUB': '/path/to/file.epub'}.

Bir çift liste döndürür: ids, duplicates. ids ver tabanında yeni oluşturulan tüm kitapların kitap id’lerini içerir. duplicates veri tabanında zaten mevcut olan ve has_book() tarafından kullanılan basit çift algılama sezgiseline göre (mi, format_map) içerir.

add_custom_book_data(name, val_map, delete_first=False)[kaynak]

val_map book_ids’in değerlere haritası olmak üzere isimler için veri ekle. delete_first True ise, isim için daha önce kaydedilen tüm veri silinir.

add_extra_files(book_id, map_of_relpath_to_stream_or_path, replace=True, auto_rename=False)[kaynak]

Ekstra veri dosyaları ekleyin

add_format(book_id, fmt, stream_or_path, replace=True, run_hooks=True, dbapi=None)[kaynak]

Belirtilen kitaba bir biçim ekle. Biçim başarıyla eklenmişse Doğru olarak döndür.

Parametreler:

- replace – True ise varolan biçimi değiştir, eğer biçim zaten mevcutsa, False döndür.

- run_hooks – True ise, dosya türü eklentiler eklendikten önce ve sonra biçim üzerinde çalıştırılır.

- dbapi – Sadece dahili kullanım için.

add_listener(event_callback_function, check_already_added=False)[kaynak]

Bu veritabanında belirli eylemler gerçekleştirildikten sonra çağrılacak bir geri çağırma işlevini kaydedin. İşlev üç bağımsız değişken almalıdır: (:sınıf:`EventType`, kitaplık_id, olay_tipi_özel_veri)

add_notes_resource(path_or_stream_or_data, name: str, mtime: float = None) → int[kaynak]

Add the specified resource so it can be referenced by notes and return its content hash

all_book_ids(type=<class 'frozenset'>)[kaynak]

Bilinen tüm kitap id’lerinin dondurulmuş kümesi.

all_field_for(field, book_ids, default_value=None)[kaynak]

field_for’un aynısı, farkı birden ço kitap üstünde tek seferde çalışması

all_field_ids(name)[kaynak]

name alanındaki tüm değerler için id’lerin donmuş kümesi.

all_field_names(field)[kaynak]

Tüm alan isimlerinin dondurulmuş kümesi (sadece çoktan-bire ve çoktan-çoka alanlarda kullanılmalı)

author_data(author_ids=None)[kaynak]

Yazar verisini isim, sıralama, bağlantı anahtarlarından oluşan sözlük olarak döndür

Belirtilen id’ye sahip yazar bulunamazsa boş bör sözlük döndürülür. author_id None ise, tüm authorların verisi döndürülür.

author_sort_from_authors(authors, key_func=<function make_change_case_func.<locals>.change_case>)[kaynak]

Verilen bir yazar listesinden hesaplanan karakter dizisi üstündeki yazarla bağlantılı yazarı tercih ederek yazarlar için author_sort karakter dizisini döndür.

books_for_field(name, item_id)[kaynak]

Öğe name alanına ait olmak üzere, item_id ile tanımlı öğe ile bağlantılı tüm kitapları döndür.

Döndürülen değer kitap id’leri kümesi, veya öğe veya alan mevcut değilse boş bir kümedir.

books_in_virtual_library(vl, search_restriction=None, virtual_fields=None)[kaynak]

Belirlenen sanal kitaplıktaki kitap setini döndürür

compress_covers(book_ids, jpeg_quality=100, progress_callback=None)[kaynak]

Belirtilen kitapların kapak resimlerini sıkıştırın. 100 sıkıştırma kalitesi, kayıpsız sıkıştırma gerçekleştirir, aksi takdirde kayıplı sıkıştırma gerçekleştirir.

İlerleme geri çağrısı, işlenen her kitap içinbook_id ve eski ve yeni boyutlarla çağrılacaktır. Bir hata oluşursa yeni boyut, hata ayrıntılarını içeren bir dize olacaktır.

copy_cover_to(book_id, dest, use_hardlink=False, report_file_size=None)[kaynak]

Kapağı dosya benzeri nesne ``dest``e kopyala. Kapak mevcut değilse veya dest mevcut kapakla aynı dosya ise False döndürür. dest, yalnızca yol mevcut yoldan farklı ise kapağın içine kopyalandığı bir yol olabilir (büyük küçük harf duyarlılığı da dikkate alınarak).

copy_format_to(book_id, fmt, dest, use_hardlink=False, report_file_size=None)[kaynak]

Fmt biçimini, dest nesnesi gibi bir dosyaya kopyalayın. Belirtilen format mevcut değilse NoSuchFormat hatası ortaya çıkar. hedef aynı zamanda bir yol da olabilir (bir dosyaya), bu durumda yol geçerli yoldan farklıysa (büyük/küçük harf duyarlılığı dikkate alınarak) format ona kopyalanır.

cover(book_id, as_file=False, as_image=False, as_path=False, as_pixmap=False)[kaynak]

Kapak resmini veya None döndür. Varsayılan olarak kapağı bytestring olarak döndürür.

UYARI: as_path kullanmak kapağı geçici bir dosyaya kopyalar ve geçici dosyanın yolunu döndürür. İşiniz bittiğinde geçici dosyayı silmelisiniz.

Parametreler:

- as_file – True ise resmi bir açık dosya nesnesi (SpooledTemporaryFile) olarak döndür

- as_image – True ise resmi bir QImage nesnesi oalrak döndür

- as_pixmap – True ise görüntüyü bir QPixmap nesnesi olarak döndürür

- as_path – True ise resmi geçici bir dosyaya işaret eden yol olarak döndür

data_for_find_identical_books()[kaynak]

db’ye erişimi olmayan bir işçi süreçte find_identical_books() uygulamak için kullanılabilecek veriyi döndür.

data_for_has_book()[kaynak]

has_book() içinde kullanmaya müsait veri döndür. Bu işlem db’ye erişimi olmayan bir işçi süreçte has_book() uygulamak için kullanılabilir.

delete_custom_book_data(name, book_ids=())[kaynak]

İsim için veriyi sil. Varsayılan olarak tüm veri silinir, yalnızca bazı kitap id’leri için veriyi silmek istiyorsanız, kitap id listesi verin.

delete_trash_entry(book_id, category)[kaynak]

Çöp kutusundan bir girişi silin. Burada kategori kitaplar için ‘b’ ve formatlar için ‘f’dir.

embed_metadata(book_ids, only_fmts=None, report_error=None, report_progress=None)[kaynak]

Belirtilen book_id’lerinin tüm biçimleri içindeki metadata’yı mevcut veri tabanına güncelle.

expire_old_trash()[kaynak]

Çöp kutusundaki çok eski girişlerin süresinin dolması

export_note(field, item_id) → str[kaynak]

Export the note as a single HTML document with embedded images as data: URLs

fast_field_for(field_obj, book_id, default_value=None)[kaynak]

field_for ile aynı, yalnızca alan nesnesini almak için fazladan arama yapmaktan kaçınır

field_for(name, book_id, default_value=None)[kaynak]

book_id ile belirtilen kitap için name alanının değerini döndür. Böyle bir kitap mevcut değilse veya name alanı için belirtilmiş bir değer yoksa veya böyle bir alan yoksa default_value döndürülür.

default_value title, title_sort, authors, author_sort ve series_index için kullanılmaz. Bunun sebebi bunları hep db’de değerleri olmasıdır. ``default_value``tüm özel sütunlar için kullanılır.

is_multiple alanları için döndürülen değer, hiçbir değer bulunmasa bile her zaman tanımlama gruplarıdır (başka bir deyişle, default_value göz ardı edilir). Bunun istisnası, döndürülen değerin her zaman bir sözlük olduğu tanımlayıcılardır. Döndürülen demetler her zaman bağlantı sırasına, yani oluşturuldukları sıraya göredir.

field_ids_for(name, book_id)[kaynak]

name alanının book_id ile tanımlanan kitaptaki değerleri için id’leri (demet olarak) döndür. Bir değer yoksa, ya da böyle bir kitap yoksa, veya böyle bir alan yoksa, boş bir demet döndürülür.

field_supports_notes(field=None) → bool[kaynak]

Return True iff the specified field supports notes. If field is None return frozenset of all fields that support notes.

find_identical_books(mi, search_restriction='', book_ids=None)[kaynak]

Mi’deki yazarların üst setine sahip ve aynı başlıklı (başlıklarda bulanık eşleşme kullanılır) kitapları bulur. Ayrıca data_for_find_identical_books() göz atın.

format(book_id, fmt, as_file=False, as_path=False, preserve_filename=False)[kaynak]

E-kitap biçimini bytestring olarak ya da biçim mevcut değilse, veya e-kitap dosyasına yazmaya yetkimiz yoksa None olarak döndür.

Parametreler:

- as_file – True ise e-kitap biçimi dosya nesnesi olarak döndürülür. Dosya nesnesinin SpooledTemporaryFile olduğunu not edin, yani biçimi başka bir dosyaya kopyalamak istiyorsanız performans için bunun yerine copy_format_to() kullanın.

- as_path – Biçim dosyasını geçici bir dosyaya kopyalar ve geçici dosyanın yolunu döndürür

- preserve_filename – True ise ve bir yol döndürüyorsa, dosya adı kitaplıkta kullanılanla aynıdır. Bunun kullanımının tekrar kullanımlarda aynı geçici dosyayı döndüreceğini not edin (her seferinde tekrar oluşturulan)

format_abspath(book_id, fmt)[kaynak]

format biçimindeki e-kitap için mutlak yolu döndür. API’nin iş ipi güvenliğini kırdığından bunu hemen hemen hiç kullanmamalısınız. Bunun yerine copy_format_to() kullanın.

Şu anda yalnızca calibredb listesinde, görüntüleyicide, kitap düzenlemede, orijinal formatla karşılaştırma_formatında, birlikte açmada, toplu meta veri düzenlemede ve kataloglarda (get_data_as_dict() aracılığıyla) kullanılmaktadır.

Görüntüleyici, birlikte aç ve kitap düzenleden ayrı olarak, diğerlerinin bu çağrı sonucu herhangi bir dosya yazma G/Ç yaptıklarını sanmıyorum.

format_hash(book_id, fmt)[kaynak]

Belirtilen kitap için belirtilen biçimin özetini döndür. Özet türü arka uca bağlıdır, ama genellikle SHA-256’dır.

format_metadata(book_id, fmt, allow_cache=True, update_db=False)[kaynak]

Belirtilen kitap için belirtilen biçimin yolunu, boyutunu ve değiştirilme zamanını döndürür. İlla kullanmanız gerekmiyorsa yolu kullanmamalısınız, çünkü yola erişmek bu API’nin iş ipi güvenliğini bozar. Bunun yerine copy_format_to() metodunu kullanın.

Parametreler:

- allow_cache – True ise zuladaki değerler kullanılır, aksi halde yavaş bir dosya sistemine erişim gerçekleşir. Zula değerleri bu API dışından dosya sistemine erişildiyse eski olabilir.

- update_db – True ise veri tabanındaki max_size alanı bu kitap için güncellenmiştir.

formats(book_id, verify_formats=True)[kaynak]

Belirtilen kitap için tüm biçimlerin demetini getir. verify_formats True ise, dosyanın diskte var olduğunu da doğrular.

get_all_items_that_have_notes(field_name=None) → set[int] | dict[str, set[int]][kaynak]

Return all item_ids for items that have notes in the specified field or all fields if field_name is None

get_all_link_maps_for_book(book_id)[kaynak]

Book_id tarafından tanımlanan kitabın başvuruda bulunduğu tüm alanların tüm bağlantılarını döndürür. Book_id mevcut değilse yöntem {} değerini döndürür.

Örnek: A yazarının X bağlantısına, B yazarının Y bağlantısına, S etiketinin F bağlantısına ve T etiketinin G bağlantısına sahip olduğunu varsayalım. Kitap 1’de yazar A ve T etiketi varsa, bu yöntem şunu döndürür: {‘yazarlar’:{‘A’ :’X’}, ‘etiketler’:{‘T’, ‘G’}}. 2. kitabın yazarı ne A ne de B ise ve etiketi yoksa, bu yöntem {} değerini döndürür.

Parametreler:

book_id – söz konusu kitap kimliği.

Dönüşler:

{field: {field_value, link_value}, … o kitap için boş olmayan bağlantı değerine sahip alan_değeri olan tüm alanlar için

get_categories(sort='name', book_ids=None, already_fixed=None, first_letter_sort=False)[kaynak]

Etiket Tarayıcıyı uygulamak için dahili olarak kullanılır

get_custom_book_data(name, book_ids=(), default=None)[kaynak]

İsim için veri alır. Varsayılan olarak tüm book_id’leri için veriyi döndürür, sadece bazı veriler lazımsa kitap id’si listesi verin. book_id’den değere harita döndürür. Belli bir değerin kodu çözülemezse, varsayılan değerini kullanır.

get_id_map(field)[kaynak]

Belirtilen alan için id numaralarından değerlere harita döndür. Alan bir çoktan teke veya çoktan çoka alan olmalıdır, aksi durumda ValueError yükseltilir.

get_ids_for_custom_book_data(name)[kaynak]

İsmin verisi olan kitap id’lerin kümesini döndür.

get_item_id(field, item_name)[kaynak]

Öğe_adı için öğe kimliğini (büyük/küçük harfe duyarlı) veya bulunamazsa Yok’u döndürün

get_item_ids(field, item_names)[kaynak]

item_name için öğe id’sini döndür (büyük küçük harf duyarsız)

get_item_name(field, item_id)[kaynak]

Belirtilen alanda item_id ile belirtilen öğe için öğe ismini döndür. Ayrıca bknz get_id_map().

get_item_name_map(field, normalize_func=None)[kaynak]

Return mapping of item values to ids

get_link_map(for_field)[kaynak]

Sağlanan alan için bağlantıların sözlüğünü döndürün.

Parametreler:

for_field – bağlantı haritasının istendiği alanın arama adı

Dönüşler:

Boş olmayan bağlantılar için {field_value:link_value, …}

get_metadata(book_id, get_cover=False, get_user_categories=True, cover_as_data=False)[kaynak]

kitap_id ile belirtilen üstveriyi :sınıf:`calibre.ekitaplar.üstveri.kitap.temel.Üstveri` nesnesi olarak döndür. Biçim listesinin doğrulanmadığını dikkate alın. Eğer kapak_al Doğru ise, kapak, ya mi.kapak olarak geçici dosyaya yol olarak ya da eğer kapak_veri_olarak Doğru ise o zaman mi.kapak_veri olarak döndürülür.

get_next_series_num_for(series, field='series', current_indices=False)[kaynak]

Sıradaki seri numara üretecini kontrol eden çeşitli tercihleri hesaba katarak belirtilen seriler için sıradaki seri indisini döndür,.

Parametreler:

- field – Seriler benzeri alan (yerleşik seriler sütunu varsayılır)

- current_indices – True ise, book_id’den mevcut series_index değerine harita döndürür.

get_notes_resource(resource_hash) → dict | None[kaynak]

Return a dict containing the resource data and name or None if no resource with the specified hash is found

get_proxy_metadata(book_id)[kaynak]

Veri tabanından değerleri yalnızca istek üstüne okuyan bir ProxyMetadata nesnesi döndürmesi dışında get_metadata() gibidir. get_metadata’dan döndürülen metadata nesnesinden az sayıda alana erişilmesi gerektiğinde oldukça hızlıdır.

get_usage_count_by_id(field)[kaynak]

Çoktan teke veya çoktan çoka alan olmak üzere belirtilen alanın tüm değerleri için id’den kullanım sayısına harita döndür.

has_book(mi)[kaynak]

Veritabanı verilen Metadata nesnesiyle aynı başlığa sahip bir girdi içeriyorsa True döndürür. Karşılaştırma büyük küçük harf duyarsızdır. Ayrıca bknz data_for_has_book().

has_format(book_id, fmt)[kaynak]

Biçim diskte mevcutsa True döndür

has_id(book_id)[kaynak]

Belirtilen book_id veri tabanında mevcutsa True döndür

import_note(field, item_id, path_to_html_file, path_is_data=False)[kaynak]

Import a previously exported note or an arbitrary HTML file as the note for the specified item

init()[kaynak]

Bu zulayı arka uçtaki veri ile ilklendir.

items_with_notes_in_book(book_id: int) → dict[str, dict[int, str]][kaynak]

Return a dict of field to items that have associated notes for that field for the specified book

link_for(field, item_id)[kaynak]

Return the link, if any, for the specified item or None if no link is found

list_extra_files(book_id, use_cache=False, pattern='') → Tuple[ExtraFile, ...][kaynak]

Kitabın dizinindeki ekstra dosyalar hakkında bilgi alın.

Parametreler:

- book_id – kitabın veritabanı kitap kimliği

- pattern – aranacak dosya adlarının düzeni. Boş desen tüm ekstra dosyalarla eşleşir. Desenlerde ayırıcı olarak / kullanılmalıdır. Veri dizini içindeki dosyaları eşleştirmek için DATA_FILE_PATTERN sabitini kullanın.

Dönüşler:

Belirtilen kalıpla eşleşen tüm ekstra dosyalardan oluşan bir grup. Tuple’ın her öğesi ExtraFile(relpath, file_path, stat_result) şeklindedir. Burada relpath, ayırıcı olarak / kullanılarak dosyanın kitap dizinine göreli yoludur. stat_result, dosyada os.stat() çağrısının sonucudur.

merge_extra_files(dest_id, src_ids, replace=False)[kaynak]

Ekstra dosyaları src_ids’den dest_id’ye birleştirin. Çakışan dosyalar, change=True olmadığı sürece otomatik olarak yeniden adlandırılır; bu durumda dosyalar değiştirilir.

move_book_from_trash(book_id)[kaynak]

Bir kitabın çöp dizininden silinmesini geri alma

move_format_from_trash(book_id, fmt)[kaynak]

Çöp dizininden silinen biçimi geri alma

multisort(fields, ids_to_sort=None, virtual_fields=None)[kaynak]

Sıralanmış kitap id’leri listesi döndür. ids_to_sort None ise, tüm kitap id’leri döndürülür.

Alanlar (field_name, ascending=True veya False) biçiminde 2’li demet listesi olmalıdır. En önemli alan ilk ikili demettir.

notes_data_for(field, item_id) → str[kaynak]

Return all notes data as a dict or None if note does not exist

notes_for(field, item_id) → str[kaynak]

Return the notes document or an empty string if not found

notes_resources_used_by(field, item_id)[kaynak]

Return the set of resource hashes of all resources used by the note for the specified item

pref(name, default=None, namespace=None)[kaynak]

Belirtilen özellik için değeri ya da tercih belirtilmemişse default olarak belirtilen değeri döndür.

read_backup(book_id)[kaynak]

Kitap için OPF metadata yedeğini bytestring olarak veya böyle bir backup yoksa None olarak döndür.

remove_books(book_ids, permanent=False)[kaynak]

Book_ids tarafından belirtilen kitapları veritabanından kaldırın ve format dosyalarını silin. Kalıcı Yanlış ise, format dosyaları kütüphane başına çöp dizinine yerleştirilir.

remove_formats(formats_map, db_only=False)[kaynak]

Seçili biçimleri seçili kitaplardan çıkar.

Parametreler:

- formats_map – Kitaptan çıkarılacak book_id’den biçim listesine harita.

- db_only – True ise, biçim kaydını sadece veri tabanından çıkar, asıl biçimi dosya sisteminden silme.

Dönüşler:

O kitabın dosya sisteminden fiilen silinen formatlar kümesinin kitap kimliğinin haritası

remove_items(field, item_ids, restrict_to_book_ids=None)[kaynak]

Belirtilen alandaki belirtilen kimliğe sahip tüm öğeleri sil. Etkilenen kitap kimlikleri kümesini döndürür. restrict_to_book_ids isteğe bağlı kitap kimlikleri listesidir. Eğer belirtilirse öğeler yalnızca bu kitaplardan çıkartılır.

rename_extra_files(book_id, map_of_relpath_to_new_relpath, replace=False)[kaynak]

Ekstra veri dosyalarını yeniden adlandırın

rename_items(field, item_id_to_new_name_map, change_index=True, restrict_to_book_ids=None)[kaynak]

Etiketler veya seriler gibi çoktan teke veya çoktan çoka alanlarından öğeleri yeniden adlandır.

Parametreler:

- change_index – Seri benzeri bir alanı yeniden adlandırırken series_index değerlerini de değiştir.

- restrict_to_book_ids – Yeniden adlandırmanın yapılacağı isteğe bağlı kitap kimlikleri kümesi, varsayılan olarak tüm kitaplar.

restore_book(book_id, mi, last_modified, path, formats, annotations=())[kaynak]

Dosya sisteminde mevcut olan bir kitabın veri tabanındaki girdisini geri yükle

restore_original_format(book_id, original_fmt)[kaynak]

Eğer varsa, belirtilen biçimi önceden kaydedilmiş ORIGINAL_FORMAT ile geri yükle. Başarılı olma durumunda True döndür. ORIGINAL_FORMAT başarılı geri yüklemenin ardından silinir.

property safe_read_lock

Güvenli okuma kilidi iş parçacığı zaten bir yazma kilidine sahipse hiçbir şey yapmayan, aksi durumda okuma kilidi edinen kilittir. Arama zulasını bileşik sütunlar önünde güncellerken oluşabilecek DowngradeLockErrors hatalarını önlemek için bu gereklidir. Arama zulasını güncellemek ayrıcalıklı bir kilit tutar, ama bileşik sütun araması paylaşılan bir kilit almaya çalışan ProxyMetadata kullanarak alan değerlerinin okunmasını içerir. Bu durumu tetikleyen başka senaryolar da olabilir.

Bu özellik her erişimde yeni bir kilit nesnesi döndürür. Bu kilit nesnesi yinelemeli değildir (performans için) ve yalnızca with cache.safe_read_lock: gibi bir with ifadesiyle kullanılmalıdır, aksi durumda kötü şeyler olacaktır.

save_original_format(book_id, fmt)[kaynak]

Mevcut herhangi ORIGINAL_FORMAT’ın üstüne yazarak belirtilen biçimin kopyasını ORIGINAL_FORMAT olarak keydet.

search(query, restriction='', virtual_fields=None, book_ids=None)[kaynak]

Veri tabanını belirtilen sorgu için ara, eşleşen kitap id’lerini döndür.

Parametreler:

- restriction – Belirtilen sorguya AND yapılan bir kısıtlama. Kısıtlamaların zulalandığını, yani bir a AND b aramasının bir kıstılı b’li a aramasından yavaş olacağını unutmayın.

- virtual_fields – Dahili kullanım (aranacak on_device gibi sanal alanlar).

- book_ids – None değilse, tüm kitaplar yerine verilen kitapların aranacağı bir kitap id kümesi.

search_notes(fts_engine_query='', use_stemming=True, highlight_start=None, highlight_end=None, snippet_size=None, restrict_to_fields=(), return_text=True, result_type=<class 'tuple'>, process_each_result=None, limit=None)[kaynak]

Search the text of notes using an FTS index. If the query is empty return all notes.

set_conversion_options(options, fmt='PIPE')[kaynak]

seçenekler {book_id:conversion_options} haritası şeklinde olmalıdır

set_cover(book_id_data_map)[kaynak]

Bu kitabın kapağını ayarlayın. Veriler bir QImage, QPixmap, dosya nesnesi veya bayt dizisi olabilir. Ayrıca Hiçbiri de olabilir, bu durumda mevcut kapak kaldırılır.

set_field(name, book_id_to_val_map, allow_case_change=True, do_path_update=True)[kaynak]

name ile belirtilen alanın değerlerini ayarla. Değişiklikten etkilenen tüm kitap id’lerinin kümesini döndürür.

Parametreler:

- book_id_to_val_map – book_id’lerin uygulanacak değerlere haritası.

- allow_case_change – True ise, çoktan-bire ve çoktan-çoka alanların büyük küçük harfleri değiştirilir. Örneğin bir kitabın tag1 etiketi varsa, ve başka bir kitap için etiketi Tag1 olarak ayarladıysanız allow_case_change True ise her iki kitap da Tag1 etiketine sahip olur, aksi durumda ikisi de tag1 etiketine s ahip olacaktır.

- do_path_update – Dahili kullanım içindir, değiştirmemelisiniz.

set_link_map(field, value_to_link_map, only_set_if_no_existing_link=False)[kaynak]

Alandaki öğe değerlerinin bağlantılarını ayarlar. Not: Bu yöntem, value_to_link_map’te olmayan değerleri değiştirmez

Parametreler:

- field – aranan isim

- value_to_link_map – dict(field_value:bağlantı, …). Bunların alan kimlikleri değil, değerler olduğunu unutmayın.

Dönüşler:

bağlantı ayarlanarak değiştirilen kitaplar

set_metadata(book_id, mi, ignore_errors=False, force_changes=False, set_title=True, set_authors=True, allow_case_change=False)[kaynak]

Metadata nesnesi `mi`den kitap `id`si için metadata ayarla
force_changes=True ayarı mi boş değerler içerse de set_metadata’nın alanları güncellemesine yol açar. Bu durumda ‘None’ ‘boş’dan ayrılmış olur. mi.XXX None ise, XXX yer değiştirilmez, aksi durumda değiştirilir. Etiketler, tanımlayıcılar, ve kapak öznitelikleri özel durumlardır. Etiketler ve tanımlayıcılar None olamayacağından force_changes true ise her zaman yer değiştirilirler. Mi’nin kitapta olmasını istediğiniz değerleri içerdiğinden emin olmalısınız. Kapaklar yeni bir kapak sağlanmışsa hep değiştirilir, ama asla silinmezler. force_changes’in başlık veya yazar ayarlarında etkisi olmadığını da unutmayın.

set_notes_for(field, item_id, doc: str, searchable_text: str = '', resource_hashes=(), remove_unused_resources=False) → int[kaynak]

Set the notes document. If the searchable text is different from the document, specify it as searchable_text. If the document references resources their hashes must be present in resource_hashes. Set remove_unused_resources to True to cleanup unused resources, note that updating a note automatically cleans up resources pertaining to that note anyway.

set_pref(name, val, namespace=None)[kaynak]

Belirtilen değere belirtilen seçeneği ayarla. Ayrıca bknz pref().

split_if_is_multiple_composite(f, val)[kaynak]

f bir bileşik sütun arama anahtarıysa ve sütun is_multiple ise v’yi boş olmayan benzersiz değerlere bölün. Karşılaştırma büyük/küçük harfe duyarlıdır. Sipariş korunmadı. Etiketler gibi proxy meta veri alanı alıcılarıyla uyumluluk için bir liste () döndürün.

tags_older_than(tag, delta=None, must_have_tag=None, must_have_authors=None)[kaynak]

Belirtilen zamandan eski tag etiketine sahip tüm kitapların id’lerini döndür. etiket karşılaştırma büyük küçük harf duyarsızdır.

Parametreler:

- delta – Bir timedelta nesnesi veya None. None ise, etiketli tüm id’ler döndürülür.

- must_have_tag – None değilse eşleşme listesi bu etikete sahip kitaplarla kısıtlanır

- must_have_authors – Yazarlar listesi. None değilse, eşleşme listesi bu yazarlara sahip kitaplarla kısıtlanacaktır (büyük küçük harf duyarlı).

unretire_note_for(field, item_id) → int[kaynak]

Unretire a previously retired note for the specified item. Notes are retired when an item is removed from the database

user_categories_for_books(book_ids, proxy_metadata_map=None)[kaynak]

Belirtilen kitaplar için kullanıcı kategorilerini döndür. proxy_metadata_map kitapların bir ProxyMetadata nesnesine sahip olduğu durumlarda performans artırmak amaçlı kullanılabilir ve tercihe bağlıdır. book_id’lerinin ilgili ProxyMetadata nesnesine haritası olmalıdırlar.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Calibre geliştirme ortamı ayarlamak »

- E-kitap düzenleme araçları için API belgeleri

E-kitap düzenleme araçları için API belgeleri

E-kitap düzenleme araçları, bir kitabı HTML + kaynak dosyalarının bir koleksiyonu olarak temsil eden bir calibre.ebooks.oeb.polish.container.Container nesnesinden ve üzerinde işlemler gerçekleştirmek için kullanılabilecek çeşitli araçlardan oluşur. konteyner. Tüm araçlar, çeşitli calibre.ebooks.oeb.polish.* modüllerindeki modül düzeyindeki işlevler biçimindedir.

Bir kitap için sahip olduğunuz bir kapsayıcı nesnenin yolu bunun gibidir:

from calibre.ebooks.oeb.polish.container import get_container
container = get_container('Path to book file', tweak_mode=True)

E-kitap editörü için bir eklenti yazıyorsanız, düzenlenen kitap için geçerli kapsayıcıyı şu şekilde alırsınız:

from calibre.gui2.tweak_book import current_container
container = current_container()
if container is None:
report_error # No book has been opened yet

Kapsayıcı nesne
class calibre.ebooks.oeb.polish.container.Container(rootpath, opfpath, log, clone_data=None)[kaynak]

Bir kap, açık bir e-kitabı, dosyalar ve bir OPF dosyasıyla dolu bir klasör olarak temsil eder. İki önemli kavram var:

- Kök klasör. Bu e-kitabın temelidir. Tüm e-kitap dosyaları bu klasörün içinde veya alt klasörlerinde bulunur.

- Adlar: Bunlar kitapların dosyalarının kök klasöre göre yollarıdır. Her zaman POSIX ayırıcıları içerirler ve alıntı yapılmazlar. Kitaptaki dosyalar için kurallı tanımlayıcılar olarak düşünülebilirler. Kapsayıcı nesnedeki yöntemlerin çoğu adlarla çalışır. Adlar her zaman NFC Unicode normal biçimindedir.

- Klonlar: konteyner nesnesi, e-kitap düzenleyicide kontrol noktalarını uygulamak için kullanılan verimli disk üzerinde klonlamayı destekler. Bunun işe yaraması için dosya sistemindeki dosyalara asla doğrudan erişmemelisiniz. Bunun yerine, kitaptaki bileşen dosyalarını okumak/yazmak için raw_data() veya open() kullanın.

Href ve isimler arasında dönüştürme yaparken bu sınıf tarafından sağlanan yöntemleri kullanın, bunlar tüm href’lerin tırnağa alındığını varsayar.

abspath_to_name(fullpath, root=None)[kaynak]

Bir mutlak yolu :attr:`root`a göre kurallanan bir isme dönüştür

Parametreler:

root – Temel klasör. Varsayılan olarak bu kapsayıcı nesnenin kökü kullanılır.

add_file(name, data, media_type=None, spine_index=None, modify_name_if_needed=False, process_manifest_item=None)[kaynak]

Bu kapsayıcıya bir dosya ekle. Dosya için girdiler otomatik olarak OPF manifestosu ve omurgaya eklenir (dosya bir metin belgeyse)

add_name_to_manifest(name, process_manifest_item=None)[kaynak]

Manifestoya belirtilen isme sahip bir dosya için girdi ekle. Manifesto kimliğini döndürür.

add_properties(name, *properties)[kaynak]

Belirtilen özellikleri ada göre tanımlanan bildirim öğesine ekleyin.

apply_unique_properties(name, *properties)[kaynak]

Belirtilen özelliklerin yalnızca ada göre tanımlanan bildirim öğesi üzerinde ayarlandığından emin olun. Özelliği tüm öğelerden kaldırmak için ad olarak Yok’u iletebilirsiniz.

book_type = 'oeb'

Kitabın türü (EPUB dosyalar için epub ve AZW3 dosyalar için azw3)

commit(outpath=None, keep_parsed=False)[kaynak]

Tüm kirli ayrıştırılmış nesneleri dosya sistemine kaydedin ve e-kitap dosyasını outpath’e yazın.

Parametreler:

- output – Kaydedilen e-kitap dosyasının yazılacağı yol. Hiçbiri yoksa orijinal kitap dosyasının yolu kullanılır.

- keep_parsed – Doğruysa, taahhüt edilen öğelerin ayrıştırılmış temsilleri önbellekte tutulur.

commit_item(name, keep_parsed=False)[kaynak]

Ayrıştırılmış bir nesneyi diske gönder (serileştirilir ve altında yatan dosyaya yazılır). Eğer keep_parsed Doğru ise, ayrıştırılmış temsil zulada tutulur. Ayrıca bknz: parsed()

dirty(name)[kaynak]

İsme karşılık gelen ayrıştırılmış nesneyi kirli olarak işaretle. Ayrıca bknz: parsed().

exists(name)[kaynak]

Kurallı ada karşılık gelen bir dosya/klasör mevcutsa doğrudur. Bu işlevin, temel işletim sistemi dosya sisteminin sınırlamalarından, özellikle de büyük/küçük harf duyarlılığından muzdarip olduğunu unutmayın. Bu nedenle, büyük/küçük harfe duyarlı olmayan bir dosya sisteminde, adın büyük/küçük harf durumu, temeldeki dosya sistemi dosyasının durumundan farklı olsa bile bu, True değerini döndürür. Ayrıca bakınız has_name()

filesize(name)[kaynak]

Belirtilen kuralsal isimle temsil edilen dosyanın boyutunu bayt cinsinden döndür. Kirli ayrıştırılmış nesneleri otomatik olarak halleder. Ayrıca bknz: parsed()

generate_item(name, id_prefix=None, media_type=None, unique_href=True)[kaynak]

Bir öğeyi manifestoya, verilen isimden türetilen href ile ekle. Href ve kimliğin benzersiz olduğunu otomatik olarak garantiler. Üretilen öğeyi döndürür.

get_file_path_for_processing(name, allow_modification=True)[kaynak]

Açık dosya nesnesi yerine bir dosya yolu döndürmesi dışında open()’a benzer.

property guide_type_map

Kılavuz türden kuralsal isme harita

has_name(name)[kaynak]

Belirtilen ile aynı kuralsal isme sahip bir dosya mevcutsa Doğru döndür. :meth:`exists`in aksine, bu yöntem her zaman büyük küçük harf duyarlıdır.

href_to_name(href, base=None)[kaynak]

Bir href’i (tabana göre) isme dönüştür. taban bir isim veya None olmalıdır, ki bu durumda self.root kullanılır.

insert_into_xml(parent, item, index=None)[kaynak]

Öğeyi girintilemeyi düzelterek üst öğeye ekle (ya da indis None ise ekle). Yalnızca kendine kapanan öğeler ile çalışır.

is_dir = False

Eğer kapsayıcı ziplenmemiş kitabı betimliyorsa(bir dizin)

iterlinks(name, get_line_numbers=True)[kaynak]

İsimdeki tüm bağlantılar üzerinde dön. Eğer get_line_numbers Doğru ise (bağlantı, satır_numarası, uzaklık) biçiminde sonuçlar üretir. Burda satır numarası bağlantının bulunduğu satır, uzaklık ise satır başından kaç karakter uzakta olduğudur. Uzaklığın, uzaklık sıfır değilse bir çok satırı kapsayabileceğini unutmayın.

make_name_unique(name)[kaynak]

Bu kitapta ‘ad’ın mevcut olmadığından emin olun. Eğer öyleyse, mevcut olmayan, değiştirilmiş bir sürümü döndürün.

manifest_has_name(name)[kaynak]

Manifestonun isme karşılık gelen bir girdisi varsa Doğru döndürür

property manifest_id_map

Manifesto kimliğinin kuralsal isimlere haritası

manifest_items_of_type(predicate)[kaynak]

Medya türü yüklemle eşleşen tüm bildirim öğelerinin adları. ‘yüklem’ medya türüyle çağrılacak olan bir küme, bir liste, bir dize veya tek bir argüman alan bir işlev olabilir.

manifest_items_with_property(property_name)[kaynak]

Belirtilen özelliğe sahip tüm bildirim öğeleri

property manifest_type_map

Manifesto ortam türünün bu ortam türünde kuralsal isimlerin listesine haritası

property mi

Bu kitabın metadata’sının Metadata nesnesi. Bu nesnenin bu özellik her istendiğinde oluşturulduğunu unutmayın, tutumlu kullanın.

name_to_abspath(name)[kaynak]

Kurallı bir adı mutlak işletim sistemine bağlı bir yola dönüştürme

name_to_href(name, base=None)[kaynak]

Bir ismi tabana göre yapılmış bir href’e çevir, base bir isim veya None olabilir ki bu durumda self.root kullanılır

property names_that_must_not_be_changed

Asla yeniden adlandırılmaması gereken adlar kümesi. E-kitap dosya biçimine bağlıdır.

property names_that_must_not_be_removed

Kaptan asla silinmemesi gereken adlar kümesi. E-kitap dosya biçimine bağlıdır.

property names_that_need_not_be_manifested

Bildiride eksik olmasına izin verilen adlar kümesi. E-kitap dosya biçimine bağlıdır.

open(name, mode='rb')[kaynak]

İsmin gösterdiği dosyayı doğrudan okuma/yazma için aç. Bunun dosyayı kirli ise göndereceğini, ve ayrıştırma zulasından sileceğini unutmayın. Ayrıştırılmış sürümüne tekrar erişmeden önce bu dosyayla işinizi bitirmelisiniz, yoksa kötü şeyler olur.

property opf

Ayrıştırılmış OPF dosyası

opf_get_or_create(name)[kaynak]

Belirtilen isme sahip ilk XML öğesini döndüren veya mevcut değilse opf:package öğesi altında oluşturarak döndüren kolaylaştırıcı yöntem.

property opf_version

OPF’nin <package> öğesinde ayarlanmış sürüm

property opf_version_parsed

OPF’nin <package>öğesinde bir tamsayı dizisi olarak ayarlanan sürüm

opf_xpath(expr)[kaynak]

XPath ifadesini OPF dosyasında değerlendiren kolaylık sağlayıcı yöntem, opf: ve dc: isim uzayı ön ekleri önceden tanımlanmıştır.

parsed(name)[kaynak]

Ada göre belirtilen dosyanın ayrıştırılmış bir temsilini döndürür. HTML ve XML dosyaları için bir lxml ağacı döndürülür. CSS dosyaları için bir css_parser stil sayfası döndürülür. Ayrıştırılan nesnelerin performans amacıyla önbelleğe alındığını unutmayın. Ayrıştırılan nesnede herhangi bir değişiklik yaparsanız kabın önbelleği güncellemesini bilmesi için dirty() çağrısını yapmalısınız. Ayrıca bakınız değiştir().

raw_data(name, decode=True, normalize_to_nfc=True)[kaynak]

İsimle belirtilen dosyayla ilişkili ham veriyi döndürür

Parametreler:

- decode – True ise ve dosya metin tabanlı bir MIME türüne sahipse, kodunu çözün ve ham baytlar yerine unicode bir nesne döndürün.

- normalize_to_nfc – True ise döndürülen evrensel kodda nesne EPUB ve AZW3 dosya biçimlerinin gerektirdiği şekilde NFC normal biçimine normelleştirilir.

relpath(path, base=None)[kaynak]

Bir mutlak yolu (işletim sistemi ayraçlarıyla) tabana göre bir yola dönüştür (varsayılan self.root).Göreceli yol bir isim değildir. Bunun için abspath_to_name() kullanın.

remove_from_spine(spine_items, remove_if_no_longer_in_spine=True)[kaynak]

Belirtilen öğeleri (kuralsal isme göre) omurgadan çıkar. Eğer remove_if_no_longer_in_spine True ise, öğeler kitaptan da silinir, sadece omurgadan değil.

remove_from_xml(item)[kaynak]

Öğeyi üstünden ayırır, girintiyi düzeltir (yalnızca kendi kendine kapanan öğelerde çalışır)

remove_item(name, remove_from_guide=True)[kaynak]

İsimle belirtilen öğeyi bu kapsayıcıdan çıkar. Bu, öğeye tüm başvuruları OPF manifestosundan, kılavuzdan ve omurgadan, ayrıca dahili zulalardan çıkarır.

rename(current_name, new_name)[kaynak]

Bir dosyayı geçerli_ad’dan yeni_ad’a yeniden adlandırır. Dosyanın bulunduğu klasör değişirse, dosya içindeki tüm bağlantıları otomatik olarak yeniden temellendirir. Ancak bu dosyaya referans verebilecek diğer dosyalardaki bağlantıların güncellenmediğini unutmayın. Bu performans içindir, bu tür güncellemelerin toplu olarak bir kez yapılması gerekir.

replace(name, obj)[kaynak]

Replace the parsed object corresponding to name with obj, which must be a similar object, i.e. an lxml tree for HTML/XML or a css_parser stylesheet for a CSS file.

replace_links(name, replace_func)[kaynak]

İsimdeki tüm bağlantıları replace_func kullanarak, ki bu URL alan ve değiştirilen URL’yi döndüren bir çağrılabilir olmalıdır, değiştir. Ayrıca gerçekten değişiklik yapıldıysa True olarak ayarlanacak bir ‘replace’ özniteliği olmalıdır. Bu gibi çağrılabilirler oluşturmanın uygun yolu LinkReplacer ve LinkRebaser sınıflarını kullanmaktır.

serialize_item(name)[kaynak]

Ayrıştırılmış bir nesneyi (kuralsal isimle tanımlanan) bayt karakter dizisine çevir. Bknz parsed().

set_spine(spine_items)[kaynak]

Omurgayı spine_items olacak şekilde ayarla, öyle ki spine_items (isim, doğrusal) biçiminde bir yinelenebilir olsun. İsimlerden biri manifestoda yoksa hata yükseltir.

property spine_items

Kitapların omurgasındaki her öğenin yolunu gösteren bir yineleyici. Ayrıca bakınız: spine_iter ve spine_items.

property spine_iter

Kitabın omurgasındaki her öğe için item, name is_linear döndüren bir yinelenebilir. item lxml öğesidir, name kuralsal dosya ismidir ve is_linear öğe doğrusalsa True’dir. Ayrıca bknz: spine_names ve spine_items.

property spine_names

name döndüren bir yinelenebilir ve kitap omurgasındaki her öğe için is_linear. Ayrıca bknz: spine_iter ve spine_items.

Bir kapsayıcıdaki bileşen dosyalarının yönetimi

calibre.ebooks.oeb.polish.replace.replace_links(container, link_map, frag_map=<function <lambda>>, replace_in_opf=False)[kaynak]

Bağlantıları kapsayıcıda dosyalarla değiştir. Kapsayıcıdaki tüm dosyaların üzerinden geçerek içlerinde belirtilen bağlantıları değiştirir.

Parametreler:

- link_map – Eski kuralsal isimden yeni kuralsal isme harita. Örneğin:{'images/old.png': 'images/new.png'}

- frag_map – İki bağımsız değişken (isim, çapa) alan ve yeni bir çapa döndüren bir çağrılabilir. HTML dosyalarındaki çapaları değiştirme ihtiyacınız varsa faydalıdır. Öntanımlı olarak, hiçbir şey yapmaz.

- replace_in_opf – False ise, OPF dosyasında bağlantılar değiştirilmez.

calibre.ebooks.oeb.polish.replace.rename_files(container, file_map)[kaynak]

Kapsayıcı içindeki dosyaları yeniden adlandır, onlara olan tüm bağlantıları da güncelle.

Parametreler:

file_map – Eski kuralsal isimden yeni kuralsal isme harita, örneğin:{'text/chapter1.html': 'chapter1.html'}.

calibre.ebooks.oeb.polish.replace.get_recommended_folders(container, names)[kaynak]

Verilen dosya isimleri için tavsiye edilen dizinleri döndür. Tavsiye aynı türde dosyaların kapsayıcı içinde nerede olduklarını temel alır. Belirli türde hiç dosya yoksa, tavsiye edilen dizin OPF dosyasını içeren dizin olarak varsayılır.

Güzel yazdırma ve ayrıştırma hatalarının otomatik düzeltilmesi

calibre.ebooks.oeb.polish.pretty.fix_html(container, raw)[kaynak]

Ham karakter dizisi olarak temsil edilen HTML’deki ayrıştırma hatalarını düzelt. Düzeltme HTML5 ayrıştırma algoritması ile yapılır.

calibre.ebooks.oeb.polish.pretty.fix_all_html(container)[kaynak]

Kapsayıcıdaki tüm HTML dosyalarındaki ayrıştırma hatalarını düzelt. Düzeltme HTML5 ayrıştırma algoritmasıyla yapılır.

calibre.ebooks.oeb.polish.pretty.pretty_html(container, name, raw)[kaynak]

Ham karakter dizisi olarak temsil edilen HTML’yi güzel yazdır

calibre.ebooks.oeb.polish.pretty.pretty_css(container, name, raw)[kaynak]

Ham karakter dizisi olarak temsil edilen CSS’yi güzel yazdır

calibre.ebooks.oeb.polish.pretty.pretty_xml(container, name, raw)[kaynak]

Ham olarak temsil edilen karakter dizisinin XML’ini güzel yazdır. name OPF’nin ismiyse, OPF’ye özel ek güzelleştirmeler yapılır.

calibre.ebooks.oeb.polish.pretty.pretty_all(container)[kaynak]

Kapsayıcıdaki tüm HTML/CSS/XML dosyalarını güzel yazdır

Kitap ceketlerinin yönetimi

calibre.ebooks.oeb.polish.jacket.remove_jacket(container)[kaynak]

Varsa, mevcut bir ceketi kaldır. Mevcut ceket yoksa False döndürür.

calibre.ebooks.oeb.polish.jacket.add_or_replace_jacket(container)[kaynak]

Ya kitap metadata’sından yeni bir ceket oluştur, ya da mevcut ceketi değiştir. Mevcut bir ceket değiştirildiyse True döndürür.

Dosyaların ayrılması ve birleştirilmesi

calibre.ebooks.oeb.polish.split.split(container, name, loc_or_xpath, before=True, totals=None)[kaynak]

name ile belirtilen dosyayı loc_ veeya _xpath ile belirtilen konumdan ayır. Ayırma otomatik olarak etkilenen dosyalardaki tüm bağlantı ve başvuruları günceller.

Parametreler:

- loc_or_xpath – //h:div[@id=”burayı_ayır”] gibi bir XPath ifadesi olmalıdır. Ayrıca önizleme panelinde dahili olarak ayırmayı uygulayan loc da olabilir.

- before – True ise ayırma tanımlanan öğeden önce olur, aksi halde sonra.

- totals – Dahili olarak kullanılır

calibre.ebooks.oeb.polish.split.multisplit(container, name, xpath, before=True)[kaynak]

Belirtilen dosyayı birden fazla konuma bölün (belirtilen XPath ifadesiyle eşleşen tüm etiketler). Ayrıca bakınız: split(). Bölme işlemi, etkilenen dosyalara yönelik tüm bağlantıları ve referansları otomatik olarak taşır.

Parametreler:

before – True ise ayırma tanımlanan öğeden önce yapılır, aksi halde sonra.

calibre.ebooks.oeb.polish.split.merge(container, category, names, master)[kaynak]

Belirtilen dosyaları tek bir dosya olarak birleştir, etkilenen dosyalardaki tüm bağlantı ve başvuruları otomatik olarak güncelle. Dosyalar ya tamamen HTML ya da CSS olmalıdır.

Parametreler:

- category – Ya HTML dosyalar için 'text' ya da CSS dosyalar için 'styles' olmalıdır.

- names – Birleştirilecek dosyaların listesi

- master – Birleştirilen dosyalardan hangisinin ana dosya olduğu, yani, birleştirme sonrası kalacak olan dosya.

Kapakların yönetimi

calibre.ebooks.oeb.polish.cover.set_cover(container, cover_path, report=None, options=None)[kaynak]

Kitabın kapağını cover_path ile gösterilen resim olarak ayarla.

Parametreler:

- cover_path – Ya bir resim dosyasına mutlak yol ya da kitap içindeki bir resmin kuralsal ismi. Kitaptaki bir resim kullanılırken, ayrıca seçenekleri de belirtmelisiniz, aşağıya göz atın.

- report – Tek bir bağımsız değişken alan isteğe bağlı çağrılabilir. İşlenen görevlerle ilgili bilgi ile çağrılacaktır.

- options – None veya kapağın nasıl ayarlandığını kontrol eden bir sözlük. Sözlüğün girdileri olabilir:keep_aspect: True veya False (EPUB kapaklarının en-boy oranlarını korur) no_svg: True veeya False (EPUB başlık sayfasında bir SVG kapak sargısı kullanır) existing: True veya False (cover_path kitaptaki mevcut bir resmi gösterir)

calibre.ebooks.oeb.polish.cover.mark_as_cover(container, name)[kaynak]

Belirtilen resmi kapak resmi olarak işaretle.

calibre.ebooks.oeb.polish.cover.mark_as_titlepage(container, name, move_to_start=True)[kaynak]

Belirtilen HTML dosyayı EPUB’ın başlık sayfası olarak işaretle.

Parametreler:

move_to_start – True ise HTML dosya omurganın başına taşınır

CSS ile Çalışmak

calibre.ebooks.oeb.polish.fonts.change_font(container, old_name, new_name=None)[kaynak]

Bir yazı tipi ailesini old_name’den new_name’ye değiştir. Yazı tipinin biçim sayfaları, biçim etiketleri ve biçim özniteliklerinde tamamen değişmesini sağlar. old_name gömülü bir yazı tipini gösteriyorsa, kaldırılır. Yazı tipi ailesini değiştirmek yerine kaldırmak için new_name’yi None olarak ayarlayabilirsiniz.

calibre.ebooks.oeb.polish.css.remove_unused_css(container, report=None, remove_unused_classes=False, merge_rules=False, merge_rules_with_identical_properties=False, remove_unreferenced_sheets=False)[kaynak]

Kullanılmayan tüm CSS kurallarını kitaptan çıkar. Kullanılmayan bir CSS kuralı herhangi bir içerikle eşleşmeyen kuraldır.

Parametreler:

- report – Tek bir bağımsız değişken alan isteğe bağlı çağrılabilir. Gerçekleştirilen işlemler hakkındaki bilgi ile çağrılır.

- remove_unused_classes – True ise, HTML’deki herhangi bir CSS kuralıyla eşleşmeyen sınıf öznitelikleri de kaldırılır.

- merge_rules – True ise aynı seçicilere sahip kurallar birleştirilir.

- merge_rules_with_identical_properties – True ise aynı özelliklere sahip kurallar birleştirilir.

- remove_unreferenced_sheets – Doğruysa, herhangi bir içerik tarafından başvurulmayan stil sayfaları kaldırılır

calibre.ebooks.oeb.polish.css.filter_css(container, properties, names=())[kaynak]

Belirtilen CSS özelliklerini kitaptaki tüm CSS kurallarından çıkar.

Parametreler:

- properties – Kaldırılacak özellikler kümesi. Örneğin: {'font-family', 'color'}.

- names – Özelliklerin çıkarılacağı dosyalar. Öntanımlı olarak kitaptaki tüm HTML ve CSS dosyalardır.

İçindekiler ile Çalışmak

calibre.ebooks.oeb.polish.toc.from_xpaths(container, xpaths, prefer_title=False)[kaynak]

Generate a Table of Contents from a list of XPath expressions. Each expression in the list corresponds to a level of the generate ToC. For example: ['//h:h1', '//h:h2', '//h:h3'] will generate a three level Table of Contents from the <h1>, <h2> and <h3> tags.

calibre.ebooks.oeb.polish.toc.from_links(container)[kaynak]

Kitaptaki bağlantılardan bir İçindekiler üret.

calibre.ebooks.oeb.polish.toc.from_files(container)[kaynak]

Kitaptaki dosyalardan İçindekiler üret.

calibre.ebooks.oeb.polish.toc.create_inline_toc(container, title=None)[kaynak]

Mevcut bir NCX İçindekiler Tablosundan satır içi (HTML) bir İçindekiler Tablosu oluşturun.

Parametreler:

title – Bu içindekiler için başlık.

Kitap düzenleme aracı
class calibre.gui2.tweak_book.plugin.Tool[kaynak]

Temeller: :py:sınıf:`nesne`

Bir Kitap Düzenle eklentisindeki araçlar için taban sınıf. Kullanışlı üyelerden bazıları:

- self.plugin: bu aracı ait olduğu calibre.customize.Plugin nesnesine başvuru.

- self. boss

- self. gui

Alt sınıflarda ezilmesi gereken metodlar:

- create_action()

- register_shortcut()

name = None

Bunu benzersiz bir isim olarak ayarlayın anahtar olarak kullanılacaktır

allowed_in_toolbar = True

True ise kullanıcı bu aracı eklenti araç çubuğuna koymayı seçebilir

allowed_in_menu = True

True ise kullanıcı bu aracı eklenti menüsüne koymayı seçebilir

toolbar_button_popup_mode = 'delayed'

Menü (eğer varsa) için açılır pencere kipi. Muhtemel değerler ‘delayed’, ‘instant’, ‘button’

property boss

calibre.gui2.tweak_book.boss.Boss nesnesi. Kullanıcı arayüzünü kontrol etmek için kullanılır.

property gui

Kullanıcı arayüzünün ana penceresi

property current_container

Düzenlenen kitabı temsil eden calibre.ebooks.oeb.polish.container.Container nesnesini döndür.

register_shortcut(qaction, unique_name, default_keys=(), short_text=None, description=None, **extra_data)[kaynak]

Belirtilen ``qaction``ı tetikleyecek bir klavye kısayolu kaydedin. Bu klavye kısayolu, düzenleyici tercihlerinin Klavye kısayolları bölümünde kullanıcı tarafından otomatik olarak özelleştirilebilir hale gelecektir.

Parametreler:

- qaction – Bir QAction nesnesi, kullanıcı tarafından ayarlanmış tuş kombinasyonuna basıldığında tetiklenecektir.

- unique_name – Bu kısayol/eylem için benzersiz bir isim. Dahili olarak kullanılacaktır, bu eklentideki diğer eylemlerde kullanılmamalıdır.

- default_keys – Öntanımlı klavye kısayollarının listesi. Belirtilmezse öntanımlı kısayol ayarlanmaz. Burada belirtilen kısayollar dahili kısayollarla ya da kullanıcı yapılandırmasıyla/diğer eklentilerle kesişirse, göz ardı edilirler. Bu durumda, kullanıcılar kısayolları elle Tercihler yardımıyla yapılandırmak zorundadırlar. Örneğin: default_keys=('Ctrl+J', 'F9').

- short_text – Bu eylemin isteğe bağlı kısa bir tanımı. Belirtilmezse QAction’daki metin kullanılır.

- description – Bu eylem için isteğe bağlı uzun tanım, bu kısayol için tercihler girdisinde kullanılacaktır.

create_action(for_toolbar=True)[kaynak]

``for_toolbar``a bağlı olarak, ya eklenti araç çubuğuna, ya da eklentiler menüsüne eklenecek olan bir QAction oluştur. Örneğin:

def create_action(self, for_toolbar=True):
ac = QAction(get_icons('myicon.png'), 'Do something')
if for_toolbar:
# We want the toolbar button to have a popup menu
menu = QMenu()
ac.setMenu(menu)
menu.addAction('Do something else')
subaction = menu.addAction('And another')

# Register a keyboard shortcut for this toolbar action be
# careful to do this for only one of the toolbar action or
# the menu action, not both.
self.register_shortcut(ac, 'some-unique-name', default_keys=('Ctrl+K',))
return ac

Ayrıca bakınız

Metod register_shortcut().

Düzenleyicinin kullanıcı arayüzünün kontrolü

E-kitap düzenleyicinin kullanıcı arayüzü tek bir global Boss nesnesi tarafından kontrol edilir. Bu, ortak görevleri gerçekleştirmek için eklenti kodunda kullanılabilecek birçok yararlı yönteme sahiptir.

class calibre.gui2.tweak_book.boss.Boss(parent, notify=None)[kaynak]

add_savepoint(msg)[kaynak]

msg ile belirtilen isimle bir geri dönüş kontrol noktası oluştur

apply_container_update_to_gui(mark_as_modified=True)[kaynak]

Mevcut kitap kapsayıcısındaki son veriyi yansıtması için kullanıcı arayüzünün tüm bileşenlerini güncelle.

Parametreler:

mark_as_modified – True ise, kitap değiştirildi olarak işaretlenir, böylece kullanıcıya çıkışta kaydetmek isteyip istemediği sorulur.

close_editor(name)[kaynak]

name ile belirtilen dosyayı düzenleyen düzenleyiciyi kapat

commit_all_editors_to_container()[kaynak]

Kullanıcının düzenleyicilerde açık dosyalarda yaptığı değişiklikleri kapsayıcılara kaydet. Bu metodu mevcut kapsayıcıda herhangi bir eylem gerçekleştirmeden çağırmalısınız

property currently_editing

Şu an düzenlenen dosya ismini veya herhangi bir dosya düzenlenmiyorsa None döndür

edit_file(name, syntax=None, use_template=None)[kaynak]

name ile belirtilen dosyayı bir düzenleyicide aç

Parametreler:

- syntax – Dosyanın ortam türü, örneğin, 'text/html'. Belirtilmemişse dosya uzantısından tahmin edilir.

- use_template – Açılan düzenleyiciyi ilklendirmek için bir şablon

open_book(path=None, edit_file=None, clear_notify_data=True, open_folder=False, search_text=None)[kaynak]

Düzenlemek için e-kitabı path konumunda açın. E-kitap desteklenen bir formatta değilse veya mevcut kitapta kaydedilmemiş değişiklikler varsa bir hata gösterecektir.

Parametreler:

edit_file – Düzenlemeye başlamak için yeni açılmış kitap içindeki bir dosyanın ismi. İsim listesi de olabilir.

rewind_savepoint()[kaynak]

Geriye alma kontrol noktasının önceki oluşumundan vazgeç, bir kontrol noktası yarattınız, ama işlemi değişiklik yapmadan iptal ettiyseniz kullanışlıdır

save_book()[kaynak]

Kitabı kaydet. Kaydetme arkaplanda yapılır

set_modified()[kaynak]

Kitabı değiştirildi olarak işaretle

show_current_diff(allow_revert=True, to_container=None)[kaynak]

Kitabın son kontrol noktasındaki durumuyla farkını göster

Parametreler:

- allow_revert – True ise fark iletişim penceresi kullanıcının tüm değişiklikleri geri almasını sağlayan bir düğme içerir

- to_container – Mevcut kapsayıcıyı karşılaştıracak bir kapsayıcı nesnesi. None ise, daha önce kontrol noktasına alınan kapsayıcı kullanılır

show_editor(name)[kaynak]

name ile belirtilen dosyayı düzenleyen düzenleyiciyi göster

sync_preview_to_editor()[kaynak]

Önizleme panelinin konumunu mevcut düzenleyicideki imleç konumuyla eşzamanla

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- sonraki |
- önceki |
- Başla »
- Dijital Haklar Yönetimi (DRM)

Dijital Haklar Yönetimi (DRM)

Dijital Haklar Yönetimi (DRM), donanım üreticileri, yayıncılar, telif hakkı sahipleri ve bireyler tarafından dijital içerik ve cihazların kullanımına sınırlamalar getirmek amacıyla kullanılabilecek erişim kontrol teknolojileri için genel bir terimdir. Aynı zamanda bazen aşağılayıcı bir şekilde Dijital Kısıtlama Yönetimi olarak da tanımlanır. Terim, dijital içeriğin içerik sağlayıcı tarafından istenmeyen veya öngörülmeyen kullanımlarını (meşru veya başka türlü) engelleyen herhangi bir teknolojiyi tanımlamak için kullanılır. Bu terim genellikle, seri numaraları veya anahtar dosyaları gibi, dosya veya aygıtta değişiklik yapılmadan atlatılabilecek diğer kopya koruma biçimlerini ifade etmez. Ayrıca dijital eserlerin veya cihazların belirli örnekleriyle ilişkili kısıtlamalara da atıfta bulunabilir. DRM teknolojileri, son kullanıcıların erişimini, kopyalanmasını veya başka formatlara dönüştürülmesini engelleyerek dijital medyanın kullanımını kontrol etmeye çalışır. Bkz. Wikipedia [https://en.wikipedia.org/wiki/Digital_rights_management].

DRM kişisel olarak benim için ne ifade ediyor?

DRM ile bir e-kitap satın aldığınızda, ona gerçekten sahip olmazsınız ancak onu satıcı tarafından size dikte edilen şekilde kullanma iznini satın almış olursunuz. DRM, “satın aldığınız” e-kitaplarla yapabileceklerinizi sınırlar. Çoğu zaman DRM ile kitap satın alan kişiler bu kısıtlamaların boyutundan habersizdir. Bu kısıtlamalar, yazı tipi boyutlarını ayarlamak gibi stilistik değişiklikler yapmak da dahil olmak üzere, e-kitabı beğeninize göre yeniden biçimlendirmenizi engeller; ancak DRM olmayan kitaplar için bu tür şeyleri yapmanıza olanak tanıyan bir yazılım vardır. İnsanlar genellikle belirli bir formatta satın aldıkları bir e-kitabın DRM’ye sahip olması durumunda başka bir formata dönüştürülememesine şaşırırlar. Yani bir Amazon Kindle’ınız varsa ve Barnes and Nobles tarafından satılan bir kitabı satın alırsanız, bilmelisiniz ki o e-kitap DRM’ye sahipse onu Kindle’ınızda okuyamazsınız. Dikkat edin, çaldığınız ya da korsan olarak satın aldığınız bir kitaptan değil, SATIN ALDIĞINIZ bir kitaptan bahsediyorum.

DRM yazarlar için ne yapar?

DRMed e-kitap yayıncıları, DRM’nin tamamen yazarların yararına olduğunu, sanatsal bütünlüklerini koruduğunu ve korsanlığı önlediğini savunuyor. Ancak DRM korsanlığı engellemez. Korsan içerik kullanmak veya korsan içerik kullanmak isteyen kişiler yine de bunu yapıyor ve başarılı oluyor. Günümüzde e-kitaplara yönelik üç ana DRM şeması Amazon, Adobe ve Barnes and Noble tarafından yürütülmektedir ve üç DRM şemasının tümü kırılmıştır. DRM’nin yaptığı tek şey meşru kullanıcılara rahatsızlık vermektir. Kitabı satın alacak kişilerin DRM’ye katlanmak istememeleri nedeniyle korsan bir versiyon bulmayı seçmeleri nedeniyle bunun aslında yazarlara zarar verdiği ileri sürülebilir. DRM’nin yokluğunda korsanlık yapacak olanlar bunu DRM’nin varlığında da yaparlar. Tekrarlamak gerekirse, önemli nokta DRM’nin korsanlığı engellememesidir. Dolayısıyla DRM, e-kitap alıcıları için yalnızca anlamsız ve zararlı değil, aynı zamanda para israfıdır.

DRM ve özgürlük

Her ne kadar dijital içerik, bilgiyi ve yaratıcı çalışmaları herkes için kolayca erişilebilir hale getirmek ve insanlığı güçlendirmek için kullanılabilse de, bu, sırf gelişmekte olan dünyayla ilişkilerini sürdürmek için insanları bu özgürlük olanağından uzaklaştırmak isteyen bazı yayıncıların çıkarına değildir. o kadar hızlı ki yetişemiyorlar.

Calibre neden DRM’yi desteklemiyor?

calibre açık kaynaklı bir yazılımdır, DRM ise doğası gereği kapalıdır. Calibre, DRM dosyalarının açılmasını veya görüntülenmesini destekliyorsa, bugünün yasalarına göre yasa dışı olan DRM’yi kaldırmak için bir araç olarak kullanılmak üzere önemsiz bir şekilde değiştirilebilir. Açık kaynaklı yazılım ve DRM, ilkelerin çatışmasıdır. DRM tamamen kullanıcıyı kontrol etmekle ilgiliyken, açık kaynaklı yazılım kullanıcıyı güçlendirmekle ilgilidir. İkisi bir arada var olamaz.

Calibre’nin içerik sağlayıcılar hakkındaki görüşü nedir?

Yazarların ve diğer içerik sağlayıcıların çabalarının karşılığını alması gerektiğine kesinlikle inanıyoruz, ancak DRM bunu yapmanın yolu değildir. Çeşitli kaynaklardan alınan DRM’siz e-kitaplardan oluşan bu veritabanını, DRM’siz alternatifler bulmanıza yardımcı olmak ve DRM’siz e-kitapların bağımsız yazarları ve yayıncılarının içeriklerini duyurmasına yardımcı olmak için geliştiriyoruz. Bunu faydalı bulacağınızı umuyoruz ve burada size sunulan içeriği korsan olarak kullanmamanızı rica ediyoruz.

DRM ile savaşmaya nasıl yardımcı olabilirim?

E-kitap okuyan ve satın alan biri olarak DRM ile mücadeleye yardımcı olabilirsiniz. DRM’li e-kitap satın almayın. DRM’siz e-kitaplar yayınlayan bazı yayıncılar var. Aradığınız e-kitabı taşıyıp taşımadıklarını görmek için çaba gösterin. DRM’siz e-kitaplar satan bazı bağımsız yazarların kitaplarını seviyorsanız ve maddi gücünüz yetiyorsa onlara bağışta bulunun. Bu, iyi harcanmış bir paradır, çünkü onların e-kitapları DRMed kitap yayıncılarından satın alacağınız kitaplardan daha ucuzdur (istisnalar olabilir) ve muhtemelen gelecekte sahip olduğunuz tüm cihazlarda çalışarak sizi satın alma maliyetinden kurtaracaktır. yine e-kitap. İçeriklerini korsan olarak yayınlayarak DRM’siz e-kitapların yayıncılarını ve yazarlarını caydırmayın. İçerik sağlayıcılar çabalarının karşılığını hak ediyorlar. DRM’siz e-kitapları kullanıma sunarak okuma deneyiminizi daha iyi hale getirmeye çalıştıkları için onları cezalandırmayın. Uzun vadede bu sizin için zararlıdır. Hem DRM’li hem de DRM’siz kitaplar taşıyan satıcılardan kitaplar satın aldıysanız, bunların DRM taşıyıp taşımadığını bilmiyorsanız veya web sitesinde gelecekteki alıcıları DRM durumu hakkında bilgilendiren bir yorum veya inceleme bırakmayı bir noktaya getirmeyin. Çoğu satıcı, bir e-kitabın DRM taşıyıp taşımadığını alıcılarına açıkça belirtmenin önemli olduğunu düşünmüyor. Burada [https://www.defectivebydesign.org/guide/ebooks] DRM’siz yaşama ilişkin bir rehber bulacaksınız.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- önceki |
- Başla »
- Açıklayıcı sözlük

Açıklayıcı sözlük

RSS

RSS (Really Simple Syndication), haber makaleleri, blog gönderileri vb. gibi sık sık güncellenen içerikleri yayınlamak için kullanılan bir web besleme biçimidir. Özellikle bilgisayarlar tarafından okunmaya uygun bir biçimdir ve bu nedenle internetten bir e-kitaba içerik almanın tercih edilen yoludur. İnternette kullanımda olan başka birçok besleme biçimi vardır ve calibre bunların çoğunu destekler. Özellikle, bloglar için yaygın olarak kullanılan ATOM biçimini destekler.

tarif

Tarif, calibre’ye dergi veya blog gibi bir çevrimiçi haber kaynağının e-kitaba nasıl dönüştürüleceğini öğreten bir dizi talimattır. Bir tarif aslında Python [https://www.python.org] kodudur. Bu nedenle, keyfi olarak karmaşık haber kaynaklarını e-kitaplara dönüştürme yeteneğine sahiptir. En basit düzeyde, calibre’ye internete çıkıp haberleri indirmesi için yeterli bilgiyi veren URL’ler gibi sadece bir dizi değişkendir.

HTML

HTML (Hyper Metin İşaretleme Dili), elektronik yayıncılık için Standart Genişletilmiş İşaretleme Dili’nin (SGML) WWW için kullanılan bir alt kümesidir.

CSS

CSS (Geçişli Stil Sayfaları) bir HTML belgesinin nasıl işleneceğini (görsel tasarım) tanımlamak için kullanılan bir dildir.

API

API (Uygulama Programlama Arayüzü) bilgisayar programları tarafından yapılan servis isteklerine destek sağlayan bir kitaplık kaynak kod arayüzüdür.

LRF

LRF SONY eKitap okuyucuları tarafından okunan eKitap formatı.

Adres

URL (Birörnek Kaynak Konumlayıcı) örneğin: http://ornek.com

düzenli ifade

Düzenli ifadeler, belirli karakterler, sözcükler veya karakter kalıpları gibi ilgilenilen metin dizilerini tanımlamak için özlü ve esnek bir yol sağlar. <regexp>:doc:Düzenli ifadelere giriş için öğretici eğitimine bakın.

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Python Modül Dizini

Python Modül Dizini
c

| c |
| calibre |
| calibre.customize | Defines various abstract base classes that can be subclassed to create plugins.
| calibre.customize.conversion |
| calibre.db.cache | The API accessing and manipulating a calibre library.
| calibre.devices.interface |
| calibre.ebooks.metadata.book.base |
| calibre.ebooks.metadata.sources.base |
| calibre.ebooks.oeb.polish.container | The container object used to represent a book as a collection of its constituent HTML files.
| calibre.ebooks.oeb.polish.cover |
| calibre.ebooks.oeb.polish.css |
| calibre.ebooks.oeb.polish.jacket |
| calibre.ebooks.oeb.polish.pretty |
| calibre.ebooks.oeb.polish.replace |
| calibre.ebooks.oeb.polish.split |
| calibre.ebooks.oeb.polish.toc |
| calibre.gui2.tweak_book.boss |
| calibre.utils.formatter_functions |
| calibre.web.feeds.news | The API for writing recipes is defined by the :class:`BasicNewsRecipe`

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Genel bakış: modül kodu

Kodları mevcut bütün modüller

- PyQt6.QtCore
- PyQt6.QtWidgets
- calibre.customize

- calibre.customize.conversion

- calibre.db.cache
- calibre.devices.interface
- calibre.devices.usbms.cli
- calibre.devices.usbms.device
- calibre.devices.usbms.driver
- calibre.ebooks.metadata.book.base
- calibre.ebooks.metadata.sources.base
- calibre.ebooks.oeb.polish.container
- calibre.ebooks.oeb.polish.cover
- calibre.ebooks.oeb.polish.css
- calibre.ebooks.oeb.polish.fonts
- calibre.ebooks.oeb.polish.jacket
- calibre.ebooks.oeb.polish.pretty
- calibre.ebooks.oeb.polish.replace
- calibre.ebooks.oeb.polish.split
- calibre.ebooks.oeb.polish.toc
- calibre.gui2.actions
- calibre.gui2.preferences
- calibre.gui2.tweak_book.boss
- calibre.gui2.tweak_book.plugin
- calibre.utils.formatter_functions
- calibre.web.feeds.news

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.customize

calibre.customize öğesinin kaynak kodu

__license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'

import enum import importlib import os import sys import zipfile from calibre.constants import ismacos, iswindows, numeric_version from calibre.ptempfile import PersistentTemporaryFile if iswindows: platform = 'windows' elif ismacos: platform = 'osx' else: platform = 'linux'

class PluginNotFound(ValueError): pass class InvalidPlugin(ValueError): pass class PluginInstallationType(enum.IntEnum): EXTERNAL = 1 SYSTEM = 2 BUILTIN = 3

[belgeler] class Plugin: # {{{ ''' A calibre plugin. Useful members include:

* ``self.installation_type``: Stores how the plugin was installed. * ``self.plugin_path``: Stores path to the ZIP file that contains this plugin or None if it is a builtin plugin * ``self.site_customization``: Stores a customization string entered by the user.

Methods that should be overridden in sub classes:

* :meth:`initialize` * :meth:`customization_help`

Useful methods:

* :meth:`temporary_file` * :meth:`__enter__` * :meth:`load_resources`

''' #: List of platforms this plugin works on. #: For example: ``['windows', 'osx', 'linux']`` supported_platforms = []

#: The name of this plugin. You must set it something other #: than Trivial Plugin for it to work. name = 'Trivial Plugin'

#: The version of this plugin as a 3-tuple (major, minor, revision) version = (1, 0, 0)

#: A short string describing what this plugin does description = _('Does absolutely nothing')

#: The author of this plugin author = _('Unknown')

#: When more than one plugin exists for a filetype, #: the plugins are run in order of decreasing priority. #: Plugins with higher priority will be run first. #: The highest possible priority is ``sys.maxsize``. #: Default priority is 1. priority = 1

#: The earliest version of calibre this plugin requires minimum_calibre_version = (0, 4, 118)

#: The way this plugin is installed installation_type = None

#: If False, the user will not be able to disable this plugin. Use with #: care. can_be_disabled = True

#: The type of this plugin. Used for categorizing plugins in the #: GUI type = _('Base')

def __init__(self, plugin_path): self.plugin_path = plugin_path self.site_customization = None

[belgeler] def initialize(self): ''' Called once when calibre plugins are initialized. Plugins are re-initialized every time a new plugin is added. Also note that if the plugin is run in a worker process, such as for adding books, then the plugin will be initialized for every new worker process.

Perform any plugin specific initialization here, such as extracting resources from the plugin ZIP file. The path to the ZIP file is available as ``self.plugin_path``.

Note that ``self.site_customization`` is **not** available at this point. ''' pass

[belgeler] def config_widget(self): ''' Implement this method and :meth:`save_settings` in your plugin to use a custom configuration dialog, rather then relying on the simple string based default customization.

This method, if implemented, must return a QWidget. The widget can have an optional method validate() that takes no arguments and is called immediately after the user clicks OK. Changes are applied if and only if the method returns True.

If for some reason you cannot perform the configuration at this time, return a tuple of two strings (message, details), these will be displayed as a warning dialog to the user and the process will be aborted. ''' raise NotImplementedError()

[belgeler] def save_settings(self, config_widget): ''' Save the settings specified by the user with config_widget.

:param config_widget: The widget returned by :meth:`config_widget`.

''' raise NotImplementedError()

[belgeler] def do_user_config(self, parent=None): ''' This method shows a configuration dialog for this plugin. It returns True if the user clicks OK, False otherwise. The changes are automatically applied. ''' from qt.core import QApplication, QDialog, QDialogButtonBox, QLabel, QLineEdit, QScrollArea, QSize, Qt, QVBoxLayout from calibre.gui2 import gprefs prefname = 'plugin config dialog:'+self.type + ':' + self.name config_dialog = QDialog(parent) button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) v = QVBoxLayout(config_dialog) button_box.accepted.connect(config_dialog.accept) button_box.rejected.connect(config_dialog.reject) config_dialog.setWindowTitle(_('Customize') + ' ' + self.name) try: config_widget = self.config_widget() except NotImplementedError: config_widget = None if isinstance(config_widget, tuple): from calibre.gui2 import warning_dialog warning_dialog(parent, _('Cannot configure'), config_widget[0], det_msg=config_widget[1], show=True) return False if config_widget is not None: class SA(QScrollArea): def sizeHint(self): sz = self.widget().sizeHint() fw = 2 * self.frameWidth() return QSize(sz.width() + self.verticalScrollBar().sizeHint().width() + fw, sz.height() + fw) sa = SA(config_dialog) sa.setWidget(config_widget) sa.setWidgetResizable(True) v.addWidget(sa) v.addWidget(button_box) if not config_dialog.restore_geometry(gprefs, prefname): QApplication.instance().ensure_window_on_screen(config_dialog) config_dialog.exec()

if config_dialog.result() == QDialog.DialogCode.Accepted: if hasattr(config_widget, 'validate'): if config_widget.validate(): self.save_settings(config_widget) else: self.save_settings(config_widget) else: from calibre.customize.ui import customize_plugin, plugin_customization help_text = self.customization_help(gui=True) help_text = QLabel(help_text, config_dialog) help_text.setWordWrap(True) help_text.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByMouse | Qt.TextInteractionFlag.LinksAccessibleByKeyboard) help_text.setOpenExternalLinks(True) v.addWidget(help_text) sc = plugin_customization(self) if not sc: sc = '' sc = sc.strip() sc = QLineEdit(sc, config_dialog) v.addWidget(sc) v.addWidget(button_box) config_dialog.restore_geometry(gprefs, prefname) config_dialog.exec()

if config_dialog.result() == QDialog.DialogCode.Accepted: sc = str(sc.text()).strip() customize_plugin(self, sc)

config_dialog.save_geometry(gprefs, prefname) return config_dialog.result()

[belgeler] def load_resources(self, names): ''' If this plugin comes in a ZIP file (user added plugin), this method will allow you to load resources from the ZIP file.

For example to load an image::

pixmap = QPixmap() pixmap.loadFromData(self.load_resources(['images/icon.png'])['images/icon.png']) icon = QIcon(pixmap)

:param names: List of paths to resources in the ZIP file using / as separator

:return: A dictionary of the form ``{name: file_contents}``. Any names that were not found in the ZIP file will not be present in the dictionary.

''' if self.plugin_path is None: raise ValueError('This plugin was not loaded from a ZIP file') ans = {} with zipfile.ZipFile(self.plugin_path, 'r') as zf: for candidate in zf.namelist(): if candidate in names: ans[candidate] = zf.read(candidate) return ans

[belgeler] def customization_help(self, gui=False): ''' Return a string giving help on how to customize this plugin. By default raise a :class:`NotImplementedError`, which indicates that the plugin does not require customization.

If you re-implement this method in your subclass, the user will be asked to enter a string as customization for this plugin. The customization string will be available as ``self.site_customization``.

Site customization could be anything, for example, the path to a needed binary on the user's computer.

:param gui: If True return HTML help, otherwise return plain text help.

''' raise NotImplementedError()

[belgeler] def temporary_file(self, suffix): ''' Return a file-like object that is a temporary file on the file system. This file will remain available even after being closed and will only be removed on interpreter shutdown. Use the ``name`` member of the returned object to access the full path to the created temporary file.

:param suffix: The suffix that the temporary file will have. ''' return PersistentTemporaryFile(suffix)

def is_customizable(self): try: self.customization_help() return True except NotImplementedError: return False def __enter__(self, *args): ''' Add this plugin to the python path so that it's contents become directly importable. Useful when bundling large python libraries into the plugin. Use it like this:: with plugin: import something ''' if self.plugin_path is not None: from importlib.machinery import EXTENSION_SUFFIXES from calibre.utils.zipfile import ZipFile with ZipFile(self.plugin_path) as zf: extensions = {x.lower() for x in EXTENSION_SUFFIXES} zip_safe = True for name in zf.namelist(): for q in extensions: if name.endswith(q): zip_safe = False break if not zip_safe: break if zip_safe: sys.path.append(self.plugin_path) self.sys_insertion_path = self.plugin_path else: from calibre.ptempfile import TemporaryDirectory self._sys_insertion_tdir = TemporaryDirectory('plugin_unzip') self.sys_insertion_path = self._sys_insertion_tdir.__enter__(*args) zf.extractall(self.sys_insertion_path) sys.path.append(self.sys_insertion_path)

def __exit__(self, *args): ip, it = getattr(self, 'sys_insertion_path', None), getattr(self, '_sys_insertion_tdir', None) if ip in sys.path: sys.path.remove(ip) if hasattr(it, '__exit__'): it.__exit__(*args)

[belgeler] def cli_main(self, args): ''' This method is the main entry point for your plugins command line interface. It is called when the user does: calibre-debug -r "Plugin Name". Any arguments passed are present in the args variable. ''' raise NotImplementedError('The %s plugin has no command line interface' %self.name)

# }}}

[belgeler] class FileTypePlugin(Plugin): # {{{ ''' A plugin that is associated with a particular set of file types. '''

#: Set of file types for which this plugin should be run. #: Use '*' for all file types. #: For example: ``{'lit', 'mobi', 'prc'}`` file_types = set()

#: If True, this plugin is run when books are added #: to the database on_import = False

#: If True, this plugin is run after books are added #: to the database. In this case the postimport and postadd #: methods of the plugin are called. on_postimport = False

#: If True, this plugin is run after a book is converted. #: In this case the postconvert method of the plugin is called. on_postconvert = False

#: If True, this plugin is run after a book file is deleted #: from the database. In this case the postdelete method of #: the plugin is called. on_postdelete = False

#: If True, this plugin is run just before a conversion on_preprocess = False

#: If True, this plugin is run after conversion #: on the final file produced by the conversion output plugin. on_postprocess = False type = _('File type')

[belgeler] def run(self, path_to_ebook): ''' Run the plugin. Must be implemented in subclasses. It should perform whatever modifications are required on the e-book and return the absolute path to the modified e-book. If no modifications are needed, it should return the path to the original e-book. If an error is encountered it should raise an Exception. The default implementation simply return the path to the original e-book. Note that the path to the original file (before any file type plugins are run, is available as self.original_path_to_file).

The modified e-book file should be created with the :meth:`temporary_file` method.

:param path_to_ebook: Absolute path to the e-book.

:return: Absolute path to the modified e-book. ''' # Default implementation does nothing return path_to_ebook

[belgeler] def postimport(self, book_id, book_format, db): ''' Called post import, i.e., after the book file has been added to the database. Note that this is different from :meth:`postadd` which is called when the book record is created for the first time. This method is called whenever a new file is added to a book record. It is useful for modifying the book record based on the contents of the newly added file.

:param book_id: Database id of the added book. :param book_format: The file type of the book that was added. :param db: Library database. ''' pass # Default implementation does nothing

[belgeler] def postconvert(self, book_id, book_format, db): ''' Called post conversion, i.e., after the conversion output book file has been added to the database. Note that it is run after a conversion only, not after a book is added. It is useful for modifying the book record based on the contents of the newly added file.

:param book_id: Database id of the added book. :param book_format: The file type of the book that was added. :param db: Library database. ''' pass # Default implementation does nothing

[belgeler] def postdelete(self, book_id, book_format, db): ''' Called post deletion, i.e., after the book file has been deleted from the database. Note that it is not run when a book record is deleted, only when one or more formats from the book are deleted. It is useful for modifying the book record based on the format of the deleted file.

:param book_id: Database id of the added book. :param book_format: The file type of the book that was added. :param db: Library database. ''' pass # Default implementation does nothing

[belgeler] def postadd(self, book_id, fmt_map, db): ''' Called post add, i.e. after a book has been added to the db. Note that this is different from :meth:`postimport`, which is called after a single book file has been added to a book. postadd() is called only when an entire book record with possibly more than one book file has been created for the first time. This is useful if you wish to modify the book record in the database when the book is first added to calibre.

:param book_id: Database id of the added book. :param fmt_map: Map of file format to path from which the file format was added. Note that this might or might not point to an actual existing file, as sometimes files are added as streams. In which case it might be a dummy value or a non-existent path. :param db: Library database ''' pass # Default implementation does nothing

# }}}

[belgeler] class MetadataReaderPlugin(Plugin): # {{{ ''' A plugin that implements reading metadata from a set of file types. ''' #: Set of file types for which this plugin should be run. #: For example: ``set(['lit', 'mobi', 'prc'])`` file_types = set()

supported_platforms = ['windows', 'osx', 'linux'] version = numeric_version author = 'Kovid Goyal'

type = _('Metadata reader')

def __init__(self, *args, **kwargs): Plugin.__init__(self, *args, **kwargs) self.quick = False

[belgeler] def get_metadata(self, stream, type): ''' Return metadata for the file represented by stream (a file like object that supports reading). Raise an exception when there is an error with the input data.

:param type: The type of file. Guaranteed to be one of the entries in :attr:`file_types`. :return: A :class:`calibre.ebooks.metadata.book.Metadata` object ''' return None

# }}}

[belgeler] class MetadataWriterPlugin(Plugin): # {{{ ''' A plugin that implements reading metadata from a set of file types. ''' #: Set of file types for which this plugin should be run. #: For example: ``set(['lit', 'mobi', 'prc'])`` file_types = set()

supported_platforms = ['windows', 'osx', 'linux'] version = numeric_version author = 'Kovid Goyal'

type = _('Metadata writer')

def __init__(self, *args, **kwargs): Plugin.__init__(self, *args, **kwargs) self.apply_null = False

[belgeler] def set_metadata(self, stream, mi, type): ''' Set metadata for the file represented by stream (a file like object that supports reading). Raise an exception when there is an error with the input data.

:param type: The type of file. Guaranteed to be one of the entries in :attr:`file_types`. :param mi: A :class:`calibre.ebooks.metadata.book.Metadata` object ''' pass

# }}}

[belgeler] class CatalogPlugin(Plugin): # {{{ ''' A plugin that implements a catalog generator. '''

resources_path = None

#: Output file type for which this plugin should be run. #: For example: 'epub' or 'xml' file_types = set()

type = _('Catalog generator')

#: CLI parser options specific to this plugin, declared as `namedtuple` `Option`: #: #: from collections import namedtuple #: Option = namedtuple('Option', 'option, default, dest, help') #: cli_options = [Option('--catalog-title', default = 'My Catalog', #: dest = 'catalog_title', help = (_('Title of generated catalog. \nDefault:') + " '" + '%default' + "'"))] #: cli_options parsed in calibre.db.cli.cmd_catalog:option_parser() #: cli_options = []

def _field_sorter(self, key): ''' Custom fields sort after standard fields ''' if key.startswith('#'): return '~%s' % key[1:] else: return key def search_sort_db(self, db, opts):

db.search(opts.search_text)

if getattr(opts, 'sort_by', None): # 2nd arg = ascending db.sort(opts.sort_by, True) return db.get_data_as_dict(ids=opts.ids)

def get_output_fields(self, db, opts): # Return a list of requested fields all_std_fields = {'author_sort','authors','comments','cover','formats', 'id','isbn','library_name','ondevice','pubdate','publisher', 'rating','series_index','series','size','tags','timestamp', 'title_sort','title','uuid','languages','identifiers'} all_custom_fields = set(db.custom_field_keys()) for field in list(all_custom_fields): fm = db.field_metadata[field] if fm['datatype'] == 'series': all_custom_fields.add(field+'_index') all_fields = all_std_fields.union(all_custom_fields)

if getattr(opts, 'fields', 'all') != 'all': # Make a list from opts.fields of = [x.strip() for x in opts.fields.split(',')] requested_fields = set(of)

# Validate requested_fields if requested_fields - all_fields: from calibre.library import current_library_name invalid_fields = sorted(list(requested_fields - all_fields)) print("invalid --fields specified: %s" % ', '.join(invalid_fields)) print("available fields in '%s': %s" % (current_library_name(), ', '.join(sorted(list(all_fields))))) raise ValueError("unable to generate catalog with specified fields")

fields = [x for x in of if x in all_fields] else: fields = sorted(all_fields, key=self._field_sorter)

if not opts.connected_device['is_device_connected'] and 'ondevice' in fields: fields.pop(int(fields.index('ondevice')))

return fields

[belgeler] def initialize(self): ''' If plugin is not a built-in, copy the plugin's .ui and .py files from the ZIP file to $TMPDIR. Tab will be dynamically generated and added to the Catalog Options dialog in calibre.gui2.dialogs.catalog.py:Catalog ''' from calibre.customize.builtins import plugins as builtin_plugins from calibre.customize.ui import config from calibre.ptempfile import PersistentTemporaryDirectory if type(self) not in builtin_plugins and self.name not in config['disabled_plugins']: files_to_copy = [f"{self.name.lower()}.{ext}" for ext in ["ui","py"]] resources = zipfile.ZipFile(self.plugin_path,'r')

if self.resources_path is None: self.resources_path = PersistentTemporaryDirectory('_plugin_resources', prefix='')

for file in files_to_copy: try: resources.extract(file, self.resources_path) except: print(f" customize:__init__.initialize(): {file} not found in {os.path.basename(self.plugin_path)}") continue resources.close()

[belgeler] def run(self, path_to_output, opts, db, ids, notification=None): ''' Run the plugin. Must be implemented in subclasses. It should generate the catalog in the format specified in file_types, returning the absolute path to the generated catalog file. If an error is encountered it should raise an Exception.

The generated catalog file should be created with the :meth:`temporary_file` method.

:param path_to_output: Absolute path to the generated catalog file. :param opts: A dictionary of keyword arguments :param db: A LibraryDatabase2 object ''' # Default implementation does nothing raise NotImplementedError('CatalogPlugin.generate_catalog() default ' 'method, should be overridden in subclass')

# }}}

[belgeler] class InterfaceActionBase(Plugin): # {{{ supported_platforms = ['windows', 'osx', 'linux'] author = 'Kovid Goyal' type = _('User interface action') can_be_disabled = False actual_plugin = None def __init__(self, *args, **kwargs): Plugin.__init__(self, *args, **kwargs) self.actual_plugin_ = None

[belgeler] def load_actual_plugin(self, gui): ''' This method must return the actual interface action plugin object. ''' ac = self.actual_plugin_ if ac is None: mod, cls = self.actual_plugin.split(':') ac = getattr(importlib.import_module(mod), cls)(gui, self.site_customization) self.actual_plugin_ = ac return ac

# }}}

[belgeler] class PreferencesPlugin(Plugin): # {{{

''' A plugin representing a widget displayed in the Preferences dialog.

This plugin has only one important method :meth:`create_widget`. The various fields of the plugin control how it is categorized in the UI. '''

supported_platforms = ['windows', 'osx', 'linux'] author = 'Kovid Goyal' type = _('Preferences') can_be_disabled = False

#: Import path to module that contains a class named ConfigWidget #: which implements the ConfigWidgetInterface. Used by #: :meth:`create_widget`. config_widget = None

#: Where in the list of categories the :attr:`category` of this plugin should be. category_order = 100

#: Where in the list of names in a category, the :attr:`gui_name` of this #: plugin should be name_order = 100

#: The category this plugin should be in category = None

#: The category name displayed to the user for this plugin gui_category = None

#: The name displayed to the user for this plugin gui_name = None

#: The icon for this plugin, should be an absolute path icon = None

#: The description used for tooltips and the like description = None

[belgeler] def create_widget(self, parent=None): ''' Create and return the actual Qt widget used for setting this group of preferences. The widget must implement the :class:`calibre.gui2.preferences.ConfigWidgetInterface`.

The default implementation uses :attr:`config_widget` to instantiate the widget. ''' base, _, wc = self.config_widget.partition(':') if not wc: wc = 'ConfigWidget' base = importlib.import_module(base) widget = getattr(base, wc) return widget(parent)

# }}}

class StoreBase(Plugin): # {{{ supported_platforms = ['windows', 'osx', 'linux'] author = 'John Schember' type = _('Store') # Information about the store. Should be in the primary language # of the store. This should not be translatable when set by # a subclass. description = _('An e-book store.') minimum_calibre_version = (0, 8, 0) version = (1, 0, 1)

actual_plugin = None

# Does the store only distribute e-books without DRM. drm_free_only = False # This is the 2 letter country code for the corporate # headquarters of the store. headquarters = '' # All formats the store distributes e-books in. formats = [] # Is this store on an affiliate program? affiliate = False def load_actual_plugin(self, gui): ''' This method must return the actual interface action plugin object. ''' mod, cls = self.actual_plugin.split(':') self.actual_plugin_object = getattr(importlib.import_module(mod), cls)(gui, self.name) return self.actual_plugin_object def customization_help(self, gui=False): if getattr(self, 'actual_plugin_object', None) is not None: return self.actual_plugin_object.customization_help(gui) raise NotImplementedError()

def config_widget(self): if getattr(self, 'actual_plugin_object', None) is not None: return self.actual_plugin_object.config_widget() raise NotImplementedError()

def save_settings(self, config_widget): if getattr(self, 'actual_plugin_object', None) is not None: return self.actual_plugin_object.save_settings(config_widget) raise NotImplementedError()

# }}}

class EditBookToolPlugin(Plugin): # {{{ type = _('Edit book tool') minimum_calibre_version = (1, 46, 0)

# }}}

class LibraryClosedPlugin(Plugin): # {{{ ''' LibraryClosedPlugins are run when a library is closed, either at shutdown, when the library is changed, or when a library is used in some other way. At the moment these plugins won't be called by the CLI functions. ''' type = _('Library closed')

# minimum version 2.54 because that is when support was added minimum_calibre_version = (2, 54, 0)

def run(self, db): ''' The db will be a reference to the new_api (db.cache.py).

The plugin must run to completion. It must not use the GUI, threads, or any signals. ''' raise NotImplementedError('LibraryClosedPlugin ' 'run method must be overridden in subclass') # }}}

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.customize »
- calibre.customize.conversion

calibre.customize.conversion öğesinin kaynak kodu

''' Defines the plugin system for conversions. ''' import numbers import os import re import shutil from calibre import CurrentDir from calibre.customize import Plugin class ConversionOption:

''' Class representing conversion options '''

def __init__(self, name=None, help=None, long_switch=None, short_switch=None, choices=None): self.name = name self.help = help self.long_switch = long_switch self.short_switch = short_switch self.choices = choices if self.long_switch is None: self.long_switch = self.name.replace('_', '-')

self.validate_parameters()

def validate_parameters(self): ''' Validate the parameters passed to :meth:`__init__`. ''' if re.match(r'[a-zA-Z_]([a-zA-Z0-9_])*', self.name) is None: raise ValueError(self.name + ' is not a valid Python identifier') if not self.help: raise ValueError('You must set the help text')

def __hash__(self): return hash(self.name)

def __eq__(self, other): return self.name == getattr(other, 'name', other)

def clone(self): return ConversionOption(name=self.name, help=self.help, long_switch=self.long_switch, short_switch=self.short_switch, choices=self.choices)

class OptionRecommendation: LOW = 1 MED = 2 HIGH = 3 def __init__(self, recommended_value=None, level=LOW, **kwargs): ''' An option recommendation. That is, an option as well as its recommended value and the level of the recommendation. ''' self.level = level self.recommended_value = recommended_value self.option = kwargs.pop('option', None) if self.option is None: self.option = ConversionOption(**kwargs)

self.validate_parameters()

@property def help(self): return self.option.help def clone(self): return OptionRecommendation(recommended_value=self.recommended_value, level=self.level, option=self.option.clone())

def validate_parameters(self): if self.option.choices and self.recommended_value not in \ self.option.choices: raise ValueError('OpRec: %s: Recommended value not in choices'% self.option.name) if not (isinstance(self.recommended_value, (numbers.Number, bytes, str)) or self.recommended_value is None): raise ValueError('OpRec: %s:'%self.option.name + repr( self.recommended_value) + ' is not a string or a number')

class DummyReporter:

def __init__(self): self.cancel_requested = False def __call__(self, percent, msg=''): pass def gui_configuration_widget(name, parent, get_option_by_name, get_option_help, db, book_id, for_output=True): import importlib def widget_factory(cls): return cls(parent, get_option_by_name, get_option_help, db, book_id)

if for_output: try: output_widget = importlib.import_module( 'calibre.gui2.convert.'+name) pw = output_widget.PluginWidget pw.ICON = 'back.png' pw.HELP = _('Options specific to the output format.') return widget_factory(pw) except ImportError: pass else: try: input_widget = importlib.import_module( 'calibre.gui2.convert.'+name) pw = input_widget.PluginWidget pw.ICON = 'forward.png' pw.HELP = _('Options specific to the input format.') return widget_factory(pw) except ImportError: pass return None

[belgeler] class InputFormatPlugin(Plugin):

''' InputFormatPlugins are responsible for converting a document into HTML+OPF+CSS+etc. The results of the conversion *must* be encoded in UTF-8. The main action happens in :meth:`convert`. '''

type = _('Conversion input') can_be_disabled = False supported_platforms = ['windows', 'osx', 'linux'] commit_name = None # unique name under which options for this plugin are saved ui_data = None

#: Set of file types for which this plugin should be run #: For example: ``set(['azw', 'mobi', 'prc'])`` file_types = set()

#: If True, this input plugin generates a collection of images, #: one per HTML file. This can be set dynamically, in the convert method #: if the input files can be both image collections and non-image collections. #: If you set this to True, you must implement the get_images() method that returns #: a list of images. is_image_collection = False

#: Number of CPU cores used by this plugin. #: A value of -1 means that it uses all available cores core_usage = 1

#: If set to True, the input plugin will perform special processing #: to make its output suitable for viewing for_viewer = False

#: The encoding that this input plugin creates files in. A value of #: None means that the encoding is undefined and must be #: detected individually output_encoding = 'utf-8'

#: Options shared by all Input format plugins. Do not override #: in sub-classes. Use :attr:`options` instead. Every option must be an #: instance of :class:`OptionRecommendation`. common_options = { OptionRecommendation(name='input_encoding', recommended_value=None, level=OptionRecommendation.LOW, help=_('Specify the character encoding of the input document. If ' 'set this option will override any encoding declared by the ' 'document itself. Particularly useful for documents that ' 'do not declare an encoding or that have erroneous ' 'encoding declarations.') )}

#: Options to customize the behavior of this plugin. Every option must be an #: instance of :class:`OptionRecommendation`. options = set()

#: A set of 3-tuples of the form #: (option_name, recommended_value, recommendation_level) recommendations = set()

def __init__(self, *args): Plugin.__init__(self, *args) self.report_progress = DummyReporter()

[belgeler] def get_images(self): ''' Return a list of absolute paths to the images, if this input plugin represents an image collection. The list of images is in the same order as the spine and the TOC. ''' raise NotImplementedError()

[belgeler] def convert(self, stream, options, file_ext, log, accelerators): ''' This method must be implemented in sub-classes. It must return the path to the created OPF file or an :class:`OEBBook` instance. All output should be contained in the current folder. If this plugin creates files outside the current folder they must be deleted/marked for deletion before this method returns.

:param stream: A file like object that contains the input file. :param options: Options to customize the conversion process. Guaranteed to have attributes corresponding to all the options declared by this plugin. In addition, it will have a verbose attribute that takes integral values from zero upwards. Higher numbers mean be more verbose. Another useful attribute is ``input_profile`` that is an instance of :class:`calibre.customize.profiles.InputProfile`. :param file_ext: The extension (without the .) of the input file. It is guaranteed to be one of the `file_types` supported by this plugin. :param log: A :class:`calibre.utils.logging.Log` object. All output should use this object. :param accelarators: A dictionary of various information that the input plugin can get easily that would speed up the subsequent stages of the conversion.

''' raise NotImplementedError()

def __call__(self, stream, options, file_ext, log, accelerators, output_dir): try: log('InputFormatPlugin: %s running'%self.name) if hasattr(stream, 'name'): log('on', stream.name) except: # In case stdout is broken pass with CurrentDir(output_dir): for x in os.listdir('.'): shutil.rmtree(x) if os.path.isdir(x) else os.remove(x)

ret = self.convert(stream, options, file_ext, log, accelerators)

return ret

[belgeler] def postprocess_book(self, oeb, opts, log): ''' Called to allow the input plugin to perform postprocessing after the book has been parsed. ''' pass

[belgeler] def specialize(self, oeb, opts, log, output_fmt): ''' Called to allow the input plugin to specialize the parsed book for a particular output format. Called after postprocess_book and before any transforms are performed on the parsed book. ''' pass

[belgeler] def gui_configuration_widget(self, parent, get_option_by_name, get_option_help, db, book_id=None): ''' Called to create the widget used for configuring this plugin in the calibre GUI. The widget must be an instance of the PluginWidget class. See the builtin input plugins for examples. ''' name = self.name.lower().replace(' ', '_') return gui_configuration_widget(name, parent, get_option_by_name, get_option_help, db, book_id, for_output=False)

[belgeler] class OutputFormatPlugin(Plugin):

''' OutputFormatPlugins are responsible for converting an OEB document (OPF+HTML) into an output e-book.

The OEB document can be assumed to be encoded in UTF-8. The main action happens in :meth:`convert`. '''

type = _('Conversion output') can_be_disabled = False supported_platforms = ['windows', 'osx', 'linux'] commit_name = None # unique name under which options for this plugin are saved ui_data = None

#: The file type (extension without leading period) that this #: plugin outputs file_type = None

#: Options shared by all Input format plugins. Do not override #: in sub-classes. Use :attr:`options` instead. Every option must be an #: instance of :class:`OptionRecommendation`. common_options = { OptionRecommendation(name='pretty_print', recommended_value=False, level=OptionRecommendation.LOW, help=_('If specified, the output plugin will try to create output ' 'that is as human readable as possible. May not have any effect ' 'for some output plugins.') )}

#: Options to customize the behavior of this plugin. Every option must be an #: instance of :class:`OptionRecommendation`. options = set()

#: A set of 3-tuples of the form #: (option_name, recommended_value, recommendation_level) recommendations = set()

@property def description(self): return _('Convert e-books to the %s format')%self.file_type.upper()

def __init__(self, *args): Plugin.__init__(self, *args) self.report_progress = DummyReporter()

[belgeler] def convert(self, oeb_book, output, input_plugin, opts, log): ''' Render the contents of `oeb_book` (which is an instance of :class:`calibre.ebooks.oeb.OEBBook`) to the file specified by output.

:param output: Either a file like object or a string. If it is a string it is the path to a folder that may or may not exist. The output plugin should write its output into that folder. If it is a file like object, the output plugin should write its output into the file. :param input_plugin: The input plugin that was used at the beginning of the conversion pipeline. :param opts: Conversion options. Guaranteed to have attributes corresponding to the OptionRecommendations of this plugin. :param log: The logger. Print debug/info messages etc. using this.

''' raise NotImplementedError()

@property def is_periodical(self): return self.oeb.metadata.publication_type and \ str(self.oeb.metadata.publication_type[0]).startswith('periodical:')

[belgeler] def specialize_options(self, log, opts, input_fmt): ''' Can be used to change the values of conversion options, as used by the conversion pipeline. ''' pass

[belgeler] def specialize_css_for_output(self, log, opts, item, stylizer): ''' Can be used to make changes to the CSS during the CSS flattening process.

:param item: The item (HTML file) being processed :param stylizer: A Stylizer object containing the flattened styles for item. You can get the style for any element by stylizer.style(element).

''' pass

[belgeler] def gui_configuration_widget(self, parent, get_option_by_name, get_option_help, db, book_id=None): ''' Called to create the widget used for configuring this plugin in the calibre GUI. The widget must be an instance of the PluginWidget class. See the builtin output plugins for examples. ''' name = self.name.lower().replace(' ', '_') return gui_configuration_widget(name, parent, get_option_by_name, get_option_help, db, book_id, for_output=True)

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.db.cache

calibre.db.cache öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import hashlib import operator import os import random import shutil import sys import traceback import weakref from collections import defaultdict from collections.abc import MutableSet, Set from functools import partial, wraps from io import DEFAULT_BUFFER_SIZE, BytesIO from queue import Queue from threading import Lock from time import mktime, monotonic, sleep, time from typing import NamedTuple, Optional, Tuple from calibre import as_unicode, detect_ncpus, isbytestring from calibre.constants import iswindows, preferred_encoding from calibre.customize.ui import run_plugins_on_import, run_plugins_on_postadd, run_plugins_on_postdelete, run_plugins_on_postimport from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list from calibre.db.annotations import merge_annotations from calibre.db.categories import get_categories from calibre.db.constants import NOTES_DIR_NAME from calibre.db.errors import NoSuchBook, NoSuchFormat from calibre.db.fields import IDENTITY, InvalidLinkTable, create_field from calibre.db.lazy import FormatMetadata, FormatsList, ProxyMetadata from calibre.db.listeners import EventDispatcher, EventType from calibre.db.locking import DowngradeLockError, LockingError, SafeReadLock, create_locks, try_lock from calibre.db.notes.connect import copy_marked_up_text from calibre.db.search import Search from calibre.db.tables import VirtualTable from calibre.db.utils import type_safe_sort_key_function from calibre.db.write import get_series_values, uniq from calibre.ebooks import check_ebook_format from calibre.ebooks.metadata import author_to_author_sort, string_to_authors from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre.ptempfile import PersistentTemporaryFile, SpooledTemporaryFile, base_dir from calibre.utils.config import prefs, tweaks from calibre.utils.date import UNDEFINED_DATE, timestampfromdt, utcnow from calibre.utils.date import now as nowf from calibre.utils.filenames import make_long_path_useable from calibre.utils.icu import lower as icu_lower from calibre.utils.icu import sort_key from calibre.utils.localization import canonicalize_lang from polyglot.builtins import cmp, iteritems, itervalues, string_or_bytes class ExtraFile(NamedTuple): relpath: str file_path: str stat_result: os.stat_result def api(f): f.is_cache_api = True return f def read_api(f): f = api(f) f.is_read_api = True return f def write_api(f): f = api(f) f.is_read_api = False return f def wrap_simple(lock, func): @wraps(func) def call_func_with_lock(*args, **kwargs): try: with lock: return func(*args, **kwargs) except DowngradeLockError: # We already have an exclusive lock, no need to acquire a shared # lock. See the safe_read_lock properties' documentation for why # this is necessary. return func(*args, **kwargs) return call_func_with_lock def run_import_plugins(path_or_stream, fmt): fmt = fmt.lower() if hasattr(path_or_stream, 'seek'): path_or_stream.seek(0) pt = PersistentTemporaryFile('_import_plugin.'+fmt) shutil.copyfileobj(path_or_stream, pt, 1024**2) pt.close() path = pt.name else: path = path_or_stream return run_plugins_on_import(path, fmt)

def _add_newbook_tag(mi): tags = prefs['new_book_tags'] if tags: for tag in [t.strip() for t in tags]: if tag: if not mi.tags: mi.tags = [tag] elif tag not in mi.tags: mi.tags.append(tag)

def _add_default_custom_column_values(mi, fm): cols = fm.custom_field_metadata(include_composites=False) for cc,col in iteritems(cols): dv = col['display'].get('default_value', None) try: if dv is not None: if not mi.get_user_metadata(cc, make_copy=False): mi.set_user_metadata(cc, col) dt = col['datatype'] if dt == 'datetime' and icu_lower(dv) == 'now': dv = nowf() mi.set(cc, dv) except: traceback.print_exc()

dynamic_category_preferences = frozenset({'grouped_search_make_user_categories', 'grouped_search_terms', 'user_categories'})

[belgeler] class Cache:

''' An in-memory cache of the metadata.db file from a calibre library. This class also serves as a threadsafe API for accessing the database. The in-memory cache is maintained in normal form for maximum performance.

SQLITE is simply used as a way to read and write from metadata.db robustly. All table reading/sorting/searching/caching logic is re-implemented. This was necessary for maximum performance and flexibility. ''' EventType = EventType fts_indexing_sleep_time = 4 # seconds def __init__(self, backend, library_database_instance=None): self.shutting_down = False self.is_doing_rebuild_or_vacuum = False self.backend = backend self.library_database_instance = (None if library_database_instance is None else weakref.ref(library_database_instance)) self.event_dispatcher = EventDispatcher() self.fields = {} self.composites = {} self.read_lock, self.write_lock = create_locks() self.format_metadata_cache = defaultdict(dict) self.formatter_template_cache = {} self.dirtied_cache = {} self.link_maps_cache = {} self.extra_files_cache = {} self.vls_for_books_cache = None self.vls_for_books_lib_in_process = None self.vls_cache_lock = Lock() self.dirtied_sequence = 0 self.cover_caches = set() self.clear_search_cache_count = 0

# Implement locking for all simple read/write API methods # An unlocked version of the method is stored with the name starting # with a leading underscore. Use the unlocked versions when the lock # has already been acquired. for name in dir(self): func = getattr(self, name) ira = getattr(func, 'is_read_api', None) if ira is not None: # Save original function setattr(self, '_'+name, func) # Wrap it in a lock lock = self.read_lock if ira else self.write_lock setattr(self, name, wrap_simple(lock, func))

self._search_api = Search(self, 'saved_searches', self.field_metadata.get_search_terms()) self.initialize_dynamic() self.initialize_fts()

@property def new_api(self): return self

@property def library_id(self): return self.backend.library_id

@property def dbpath(self): return self.backend.dbpath

@property def is_fat_filesystem(self): return self.backend.is_fat_filesystem

@property def safe_read_lock(self): ''' A safe read lock is a lock that does nothing if the thread already has a write lock, otherwise it acquires a read lock. This is necessary to prevent DowngradeLockErrors, which can happen when updating the search cache in the presence of composite columns. Updating the search cache holds an exclusive lock, but searching a composite column involves reading field values via ProxyMetadata which tries to get a shared lock. There may be other scenarios that trigger this as well.

This property returns a new lock object on every access. This lock object is not recursive (for performance) and must only be used in a with statement as ``with cache.safe_read_lock:`` otherwise bad things will happen.''' return SafeReadLock(self.read_lock)

@write_api def ensure_has_search_category(self, fail_on_existing=True): if len(self._search_api.saved_searches.names()) > 0: self.field_metadata.add_search_category(label='search', name=_('Saved searches'), fail_on_existing=fail_on_existing)

def _initialize_dynamic_categories(self): # Reconstruct the user categories, putting them into field_metadata fm = self.field_metadata fm.remove_dynamic_categories() for user_cat in sorted(self._pref('user_categories', {}), key=sort_key): cat_name = '@' + user_cat # add the '@' to avoid name collision while cat_name: try: fm.add_user_category(label=cat_name, name=user_cat) except ValueError: break # Can happen since we are removing dots and adding parent categories ourselves cat_name = cat_name.rpartition('.')[0]

# add grouped search term user categories muc = frozenset(self._pref('grouped_search_make_user_categories', [])) for cat in sorted(self._pref('grouped_search_terms', {}), key=sort_key): if cat in muc: # There is a chance that these can be duplicates of an existing # user category. Print the exception and continue. try: self.field_metadata.add_user_category(label='@' + cat, name=cat) except ValueError: traceback.print_exc() self._ensure_has_search_category()

self.field_metadata.add_grouped_search_terms( self._pref('grouped_search_terms', {})) self._refresh_search_locations()

@write_api def initialize_dynamic(self): self.backend.dirty_books_with_dirtied_annotations() self.dirtied_cache = {x:i for i, x in enumerate(self.backend.dirtied_books())} if self.dirtied_cache: self.dirtied_sequence = max(itervalues(self.dirtied_cache))+1 self._initialize_dynamic_categories()

@write_api def initialize_template_cache(self): self.formatter_template_cache = {}

@write_api def set_user_template_functions(self, user_template_functions): self.backend.set_user_template_functions(user_template_functions)

@write_api def clear_composite_caches(self, book_ids=None): for field in itervalues(self.composites): field.clear_caches(book_ids=book_ids)

@write_api def clear_search_caches(self, book_ids=None): self.clear_search_cache_count += 1 self._search_api.update_or_clear(self, book_ids) self.vls_for_books_cache = None self.vls_for_books_lib_in_process = None

@write_api def clear_extra_files_cache(self, book_id=None): if book_id is None: self.extra_files_cache = {} else: self.extra_files_cache.pop(book_id, None)

@read_api def last_modified(self): return self.backend.last_modified()

@write_api def clear_caches(self, book_ids=None, template_cache=True, search_cache=True): if template_cache: self._initialize_template_cache() # Clear the formatter template cache for field in itervalues(self.fields): if hasattr(field, 'clear_caches'): field.clear_caches(book_ids=book_ids) # Clear the composite cache and ondevice caches if book_ids: for book_id in book_ids: self.format_metadata_cache.pop(book_id, None) else: self.format_metadata_cache.clear() if search_cache: self._clear_search_caches(book_ids) self._clear_link_map_cache(book_ids)

@write_api def clear_link_map_cache(self, book_ids=None): if book_ids is None: self.link_maps_cache = {} else: for book in book_ids: self.link_maps_cache.pop(book, None)

@write_api def reload_from_db(self, clear_caches=True): if clear_caches: self._clear_caches() with self.backend.conn: # Prevent other processes, such as calibredb from interrupting the reload by locking the db self.backend.prefs.load_from_db() self._search_api.saved_searches.load_from_db() for field in itervalues(self.fields): if hasattr(field, 'table'): field.table.read(self.backend) # Reread data from metadata.db

@property def field_metadata(self): return self.backend.field_metadata def _get_metadata(self, book_id, get_user_categories=True): # {{{ mi = Metadata(None, template_cache=self.formatter_template_cache)

mi._proxy_metadata = ProxyMetadata(self, book_id, formatter=mi.formatter)

author_ids = self._field_ids_for('authors', book_id) adata = self._author_data(author_ids) aut_list = [adata[i] for i in author_ids] aum = [] aus = {} for rec in aut_list: aut = rec['name'] aum.append(aut) aus[aut] = rec['sort'] mi.title = self._field_for('title', book_id, default_value=_('Unknown')) mi.authors = aum mi.author_sort = self._field_for('author_sort', book_id, default_value=_('Unknown')) mi.author_sort_map = aus mi.comments = self._field_for('comments', book_id) mi.publisher = self._field_for('publisher', book_id) n = utcnow() mi.timestamp = self._field_for('timestamp', book_id, default_value=n) mi.pubdate = self._field_for('pubdate', book_id, default_value=n) mi.uuid = self._field_for('uuid', book_id, default_value='dummy') mi.title_sort = self._field_for('sort', book_id, default_value=_('Unknown')) mi.last_modified = self._field_for('last_modified', book_id, default_value=n) formats = self._field_for('formats', book_id) mi.format_metadata = {} mi.languages = list(self._field_for('languages', book_id)) if not formats: good_formats = None else: mi.format_metadata = FormatMetadata(self, book_id, formats) good_formats = FormatsList(sorted(formats), mi.format_metadata) # These three attributes are returned by the db2 get_metadata(), # however, we dont actually use them anywhere other than templates, so # they have been removed, to avoid unnecessary overhead. The templates # all use _proxy_metadata. # mi.book_size = self._field_for('size', book_id, default_value=0) # mi.ondevice_col = self._field_for('ondevice', book_id, default_value='') # mi.db_approx_formats = formats mi.formats = good_formats mi.has_cover = _('Yes') if self._field_for('cover', book_id, default_value=False) else '' mi.tags = list(self._field_for('tags', book_id, default_value=())) mi.series = self._field_for('series', book_id) if mi.series: mi.series_index = self._field_for('series_index', book_id, default_value=1.0) mi.rating = self._field_for('rating', book_id) mi.set_identifiers(self._field_for('identifiers', book_id, default_value={})) mi.application_id = book_id mi.id = book_id for key, meta in self.field_metadata.custom_iteritems(): mi.set_user_metadata(key, meta) if meta['datatype'] != 'composite': # composites are evaluated on demand in metadata.book.base # because their value is None val = self._field_for(key, book_id) if isinstance(val, tuple): val = list(val) extra = self._field_for(key+'_index', book_id) mi.set(key, val=val, extra=extra)

mi.link_maps = self._get_all_link_maps_for_book(book_id)

user_cat_vals = {} if get_user_categories: user_cats = self._pref('user_categories', {}) for ucat in user_cats: res = [] for name,cat,ign in user_cats[ucat]: v = mi.get(cat, None) if isinstance(v, list): if name in v: res.append([name,cat]) elif name == v: res.append([name,cat]) user_cat_vals[ucat] = res mi.user_categories = user_cat_vals return mi # }}}

[belgeler] @api def init(self): ''' Initialize this cache with data from the backend. ''' with self.write_lock: self.backend.read_tables() bools_are_tristate = self.backend.prefs['bools_are_tristate']

for field, table in iteritems(self.backend.tables): self.fields[field] = create_field(field, table, bools_are_tristate, self.backend.get_template_functions) if table.metadata['datatype'] == 'composite': self.composites[field] = self.fields[field]

self.fields['ondevice'] = create_field('ondevice', VirtualTable('ondevice'), bools_are_tristate, self.backend.get_template_functions)

for name, field in iteritems(self.fields): if name[0] == '#' and name.endswith('_index'): field.series_field = self.fields[name[:-len('_index')]] self.fields[name[:-len('_index')]].index_field = field elif name == 'series_index': field.series_field = self.fields['series'] self.fields['series'].index_field = field elif name == 'authors': field.author_sort_field = self.fields['author_sort'] elif name == 'title': field.title_sort_field = self.fields['sort'] if self.backend.prefs['update_all_last_mod_dates_on_start']: self.update_last_modified(self.all_book_ids()) self.backend.prefs.set('update_all_last_mod_dates_on_start', False)# FTS API {{{ def initialize_fts(self): self.fts_queue_thread = None self.fts_measuring_rate = None self.fts_num_done_since_start = 0 self.fts_job_queue = Queue() self.fts_indexing_left = self.fts_indexing_total = 0 fts = self.backend.initialize_fts(weakref.ref(self)) if self.is_fts_enabled(): self.start_fts_pool() return fts def start_fts_pool(self): from threading import Event, Thread self.fts_dispatch_stop_event = Event() self.fts_queue_thread = Thread(name='FTSQueue', target=Cache.dispatch_fts_jobs, args=( self.fts_job_queue, self.fts_dispatch_stop_event, weakref.ref(self)), daemon=True) self.fts_queue_thread.start() self.backend.fts.pool.initialize() self.backend.fts.pool.initialized.wait() self.queue_next_fts_job()

@read_api def is_fts_enabled(self): return self.backend.fts_enabled

@write_api def fts_start_measuring_rate(self, measure=True): self.fts_measuring_rate = monotonic() if measure else None self.fts_num_done_since_start = 0 def _update_fts_indexing_numbers(self, job_time=None): # this is called when new formats are added and when a format is # indexed, but NOT when books or formats are deleted, so total may not # be up to date. nl = self.backend.fts.number_dirtied() nt = self.backend.get('SELECT COUNT(*) FROM main.data')[0][0] or 0 if not nl: self._fts_start_measuring_rate(measure=False) if job_time is not None and self.fts_measuring_rate is not None: self.fts_num_done_since_start += 1 if (self.fts_indexing_left, self.fts_indexing_total) != (nl, nt) or job_time is not None: self.fts_indexing_left = nl self.fts_indexing_total = nt self.event_dispatcher(EventType.indexing_progress_changed, *self._fts_indexing_progress())

@read_api def fts_indexing_progress(self): rate = None if self.fts_measuring_rate is not None and self.fts_num_done_since_start > 4: rate = self.fts_num_done_since_start / (monotonic() - self.fts_measuring_rate) return self.fts_indexing_left, self.fts_indexing_total, rate

@write_api def enable_fts(self, enabled=True, start_pool=True): fts = self.backend.enable_fts(weakref.ref(self) if enabled else None) if fts and start_pool: # used in the tests self.start_fts_pool() if not fts and self.fts_queue_thread: self.fts_job_queue.put(None) self.fts_queue_thread = None self.fts_job_queue = Queue() if fts: self._update_fts_indexing_numbers() return fts

@write_api def fts_unindex(self, book_id, fmt=None): self.backend.fts_unindex(book_id, fmt=fmt)

@staticmethod def dispatch_fts_jobs(queue, stop_dispatch, dbref): from .fts.text import is_fmt_ok def do_one(): self = dbref() if self is None: return False start_time = monotonic() with self.read_lock: if not self.backend.fts_enabled: return False book_id, fmt = self.backend.get_next_fts_job() if book_id is None: return False path = self._format_abspath(book_id, fmt) if not path or not is_fmt_ok(fmt): with self.write_lock: self.backend.remove_dirty_fts(book_id, fmt) self._update_fts_indexing_numbers() return True with self.read_lock, open(path, 'rb') as src, PersistentTemporaryFile(suffix=f'.{fmt.lower()}') as pt: sz = 0 h = hashlib.sha1() while True: chunk = src.read(DEFAULT_BUFFER_SIZE) if not chunk: break sz += len(chunk) h.update(chunk) pt.write(chunk) with self.write_lock: queued = self.backend.queue_fts_job(book_id, fmt, pt.name, sz, h.hexdigest(), start_time) if not queued: # means a dirtied book was removed from the dirty list because the text has not changed self._update_fts_indexing_numbers(monotonic() - start_time) return self.backend.fts_has_idle_workers def loop_while_more_available(): self = dbref() if not self or not self.backend.fts_enabled: return has_more = True while has_more and not self.shutting_down and self.backend.fts_enabled and not stop_dispatch.is_set(): try: has_more = do_one() except Exception: if self.backend.fts_enabled: traceback.print_exc() sleep(self.fts_indexing_sleep_time)

while not getattr(dbref(), 'shutting_down', True): x = queue.get() if x is None: break loop_while_more_available()

@write_api def queue_next_fts_job(self): if not self.backend.fts_enabled: return self.fts_job_queue.put(True) self._update_fts_indexing_numbers()

@write_api def commit_fts_result(self, book_id, fmt, fmt_size, fmt_hash, text, err_msg, start_time): ans = self.backend.commit_fts_result(book_id, fmt, fmt_size, fmt_hash, text, err_msg) self._update_fts_indexing_numbers(monotonic() - start_time) return ans

@write_api def reindex_fts_book(self, book_id, *fmts): if not self.is_fts_enabled(): return if not fmts: fmts = self._formats(book_id) self.backend.reindex_fts_book(book_id, *fmts) self._queue_next_fts_job()

@api def reindex_fts(self): if not self.is_fts_enabled(): return with self.write_lock: self._shutdown_fts() self._shutdown_fts(stage=2) with self.write_lock: self.backend.reindex_fts() fts = self.initialize_fts() fts.initialize(self.backend.conn) # ensure fts is pre-initialized needed for the tests self._queue_next_fts_job() return fts

@write_api def set_fts_num_of_workers(self, num): existing = self.backend.fts_num_of_workers if num != existing: self.backend.fts_num_of_workers = num if num > existing: self._queue_next_fts_job() return True return False

@write_api def set_fts_speed(self, slow=True): orig = self.fts_indexing_sleep_time if slow: self.fts_indexing_sleep_time = Cache.fts_indexing_sleep_time changed = self._set_fts_num_of_workers(1) else: self.fts_indexing_sleep_time = 0.1 changed = self._set_fts_num_of_workers(max(1, detect_ncpus())) changed = changed or orig != self.fts_indexing_sleep_time if changed and self.fts_measuring_rate is not None: self._fts_start_measuring_rate() return changed

@write_api # we need to use write locking as SQLITE gives a locked table error if multiple FTS queries are made at the same time def fts_search( self, fts_engine_query, use_stemming=True, highlight_start=None, highlight_end=None, snippet_size=None, restrict_to_book_ids=None, return_text=True, result_type=tuple, process_each_result=None, ): return result_type(self.backend.fts_search( fts_engine_query, use_stemming=use_stemming, highlight_start=highlight_start, highlight_end=highlight_end, snippet_size=snippet_size, return_text=return_text, restrict_to_book_ids=restrict_to_book_ids, process_each_result=process_each_result, ))

# }}}

# Notes API {{{

[belgeler] @read_api def notes_for(self, field, item_id) -> str: ' Return the notes document or an empty string if not found ' return self.backend.notes_for(field, item_id)

[belgeler] @read_api def notes_data_for(self, field, item_id) -> str: ' Return all notes data as a dict or None if note does not exist ' return self.backend.notes_data_for(field, item_id)

[belgeler] @read_api def get_all_items_that_have_notes(self, field_name=None) -> set[int] | dict[str, set[int]]: ' Return all item_ids for items that have notes in the specified field or all fields if field_name is None ' return self.backend.get_all_items_that_have_notes(field_name)

[belgeler] @read_api def field_supports_notes(self, field=None) -> bool: ' Return True iff the specified field supports notes. If field is None return frozenset of all fields that support notes. ' if field is None: return self.backend.notes.allowed_fields return field in self.backend.notes.allowed_fields

[belgeler] @read_api def items_with_notes_in_book(self, book_id: int) -> dict[str, dict[int, str]]: ' Return a dict of field to items that have associated notes for that field for the specified book ' ans = {} for k in self.backend.notes.allowed_fields: try: field = self.fields[k] except KeyError: continue v = {} for item_id in field.ids_for_book(book_id): if self.backend.notes_for(k, item_id): v[item_id] = field.table.id_map[item_id] if v: ans[k] = v return ans

[belgeler] @write_api def set_notes_for(self, field, item_id, doc: str, searchable_text: str = copy_marked_up_text, resource_hashes=(), remove_unused_resources=False) -> int: ''' Set the notes document. If the searchable text is different from the document, specify it as searchable_text. If the document references resources their hashes must be present in resource_hashes. Set remove_unused_resources to True to cleanup unused resources, note that updating a note automatically cleans up resources pertaining to that note anyway. ''' return self.backend.set_notes_for(field, item_id, doc, searchable_text, resource_hashes, remove_unused_resources)

[belgeler] @write_api def add_notes_resource(self, path_or_stream_or_data, name: str, mtime: float = None) -> int: ' Add the specified resource so it can be referenced by notes and return its content hash ' return self.backend.add_notes_resource(path_or_stream_or_data, name, mtime)

[belgeler] @read_api def get_notes_resource(self, resource_hash) -> Optional[dict]: ' Return a dict containing the resource data and name or None if no resource with the specified hash is found ' return self.backend.get_notes_resource(resource_hash)

[belgeler] @read_api def notes_resources_used_by(self, field, item_id): ' Return the set of resource hashes of all resources used by the note for the specified item ' return frozenset(self.backend.notes_resources_used_by(field, item_id))

[belgeler] @write_api def unretire_note_for(self, field, item_id) -> int: ' Unretire a previously retired note for the specified item. Notes are retired when an item is removed from the database ' return self.backend.unretire_note_for(field, item_id)

[belgeler] @read_api def export_note(self, field, item_id) -> str: ' Export the note as a single HTML document with embedded images as data: URLs ' return self.backend.export_note(field, item_id)

[belgeler] @write_api def import_note(self, field, item_id, path_to_html_file, path_is_data=False): ' Import a previously exported note or an arbitrary HTML file as the note for the specified item ' if path_is_data: html = path_to_html_file ctime = mtime = time() basedir = base_dir() else: with open(path_to_html_file, 'rb') as f: html = f.read() st = os.stat(f.fileno()) ctime, mtime = st.st_ctime, st.st_mtime basedir = os.path.dirname(os.path.abspath(path_to_html_file)) return self.backend.import_note(field, item_id, html, basedir, ctime, mtime)

[belgeler] @write_api # we need to use write locking as SQLITE gives a locked table error if multiple FTS queries are made at the same time def search_notes( self, fts_engine_query='', use_stemming=True, highlight_start=None, highlight_end=None, snippet_size=None, restrict_to_fields=(), return_text=True, result_type=tuple, process_each_result=None, limit=None, ): ' Search the text of notes using an FTS index. If the query is empty return all notes. ' return result_type(self.backend.search_notes( fts_engine_query, use_stemming=use_stemming, highlight_start=highlight_start, highlight_end=highlight_end, snippet_size=snippet_size, return_text=return_text, restrict_to_fields=restrict_to_fields, process_each_result=process_each_result, limit=limit, ))

# }}}

# Cache Layer API {{{

[belgeler] @write_api def add_listener(self, event_callback_function, check_already_added=False): ''' Register a callback function that will be called after certain actions are taken on this database. The function must take three arguments: (:class:`EventType`, library_id, event_type_specific_data) ''' self.event_dispatcher.library_id = getattr(self, 'server_library_id', self.library_id) if check_already_added and event_callback_function in self.event_dispatcher: return False self.event_dispatcher.add_listener(event_callback_function) return True

@write_api def remove_listener(self, event_callback_function): self.event_dispatcher.remove_listener(event_callback_function)

[belgeler] @read_api def field_for(self, name, book_id, default_value=None): ''' Return the value of the field ``name`` for the book identified by ``book_id``. If no such book exists or it has no defined value for the field ``name`` or no such field exists, then ``default_value`` is returned.

``default_value`` is not used for title, title_sort, authors, author_sort and series_index. This is because these always have values in the db. ``default_value`` is used for all custom columns.

The returned value for is_multiple fields are always tuples, even when no values are found (in other words, default_value is ignored). The exception is identifiers for which the returned value is always a dictionary. The returned tuples are always in link order, that is, the order in which they were created. ''' if self.composites and name in self.composites: return self.composite_for(name, book_id, default_value=default_value) try: field = self.fields[name] except KeyError: return default_value if field.is_multiple: default_value = field.default_value try: return field.for_book(book_id, default_value=default_value) except (KeyError, IndexError): return default_value

[belgeler] @read_api def fast_field_for(self, field_obj, book_id, default_value=None): ' Same as field_for, except that it avoids the extra lookup to get the field object ' if field_obj.is_composite: return field_obj.get_value_with_cache(book_id, self._get_proxy_metadata) if field_obj.is_multiple: default_value = field_obj.default_value try: return field_obj.for_book(book_id, default_value=default_value) except (KeyError, IndexError): return default_value

[belgeler] @read_api def all_field_for(self, field, book_ids, default_value=None): ' Same as field_for, except that it operates on multiple books at once ' field_obj = self.fields[field] return {book_id:self._fast_field_for(field_obj, book_id, default_value=default_value) for book_id in book_ids}

@read_api def composite_for(self, name, book_id, mi=None, default_value=''): try: f = self.fields[name] except KeyError: return default_value if mi is None: return f.get_value_with_cache(book_id, self._get_proxy_metadata) else: return f._render_composite_with_cache(book_id, mi, mi.formatter, mi.template_cache)

[belgeler] @read_api def field_ids_for(self, name, book_id): ''' Return the ids (as a tuple) for the values that the field ``name`` has on the book identified by ``book_id``. If there are no values, or no such book, or no such field, an empty tuple is returned. ''' try: return self.fields[name].ids_for_book(book_id) except (KeyError, IndexError): return ()

[belgeler] @read_api def books_for_field(self, name, item_id): ''' Return all the books associated with the item identified by ``item_id``, where the item belongs to the field ``name``.

Returned value is a set of book ids, or the empty set if the item or the field does not exist. ''' try: return self.fields[name].books_for(item_id) except (KeyError, IndexError): return set()

[belgeler] @read_api def all_book_ids(self, type=frozenset): ''' Frozen set of all known book ids. ''' return type(self.fields['uuid'].table.book_col_map)

[belgeler] @read_api def all_field_ids(self, name): ''' Frozen set of ids for all values in the field ``name``. ''' return frozenset(iter(self.fields[name]))

[belgeler] @read_api def all_field_names(self, field): ''' Frozen set of all fields names (should only be used for many-one and many-many fields) ''' if field == 'formats': return frozenset(self.fields[field].table.col_book_map)

try: return frozenset(self.fields[field].table.id_map.values()) except AttributeError: raise ValueError('%s is not a many-one or many-many field' % field)

[belgeler] @read_api def get_usage_count_by_id(self, field): ''' Return a mapping of id to usage count for all values of the specified field, which must be a many-one or many-many field. ''' try: return {k:len(v) for k, v in iteritems(self.fields[field].table.col_book_map)} except AttributeError: raise ValueError('%s is not a many-one or many-many field' % field)

[belgeler] @read_api def get_id_map(self, field): ''' Return a mapping of id numbers to values for the specified field. The field must be a many-one or many-many field, otherwise a ValueError is raised. ''' try: return self.fields[field].table.id_map.copy() except AttributeError: if field == 'title': return self.fields[field].table.book_col_map.copy() raise ValueError('%s is not a many-one or many-many field' % field)

[belgeler] @read_api def get_item_name(self, field, item_id): ''' Return the item name for the item specified by item_id in the specified field. See also :meth:`get_id_map`.''' return self.fields[field].table.id_map[item_id]

[belgeler] @read_api def get_item_id(self, field, item_name): ' Return the item id for item_name (case-insensitive) or None if not found ' q = icu_lower(item_name) try: for item_id, item_val in self.fields[field].table.id_map.items(): if icu_lower(item_val) == q: return item_id except KeyError: return None

[belgeler] @read_api def get_item_ids(self, field, item_names): ' Return the item id for item_name (case-insensitive) ' rmap = {icu_lower(v) if isinstance(v, str) else v:k for k, v in iteritems(self.fields[field].table.id_map)} return {name:rmap.get(icu_lower(name) if isinstance(name, str) else name, None) for name in item_names}

[belgeler] @read_api def get_item_name_map(self, field, normalize_func=None): ' Return mapping of item values to ids ' if normalize_func is None: return {v:k for k, v in self.fields[field].table.id_map.items()} return {normalize_func(v):k for k, v in self.fields[field].table.id_map.items()}

[belgeler] @read_api def author_data(self, author_ids=None): ''' Return author data as a dictionary with keys: name, sort, link

If no authors with the specified ids are found an empty dictionary is returned. If author_ids is None, data for all authors is returned. ''' af = self.fields['authors'] if author_ids is None: return {aid:af.author_data(aid) for aid in af.table.id_map} return {aid:af.author_data(aid) for aid in author_ids if aid in af.table.id_map}

[belgeler] @read_api def format_hash(self, book_id, fmt): ''' Return the hash of the specified format for the specified book. The kind of hash is backend dependent, but is usually SHA-256. ''' try: name = self.fields['formats'].format_fname(book_id, fmt) path = self._field_for('path', book_id).replace('/', os.sep) except: raise NoSuchFormat('Record %d has no fmt: %s'%(book_id, fmt)) return self.backend.format_hash(book_id, fmt, name, path)

[belgeler] @api def format_metadata(self, book_id, fmt, allow_cache=True, update_db=False): ''' Return the path, size and mtime for the specified format for the specified book. You should not use path unless you absolutely have to, since accessing it directly breaks the threadsafe guarantees of this API. Instead use the :meth:`copy_format_to` method.

:param allow_cache: If ``True`` cached values are used, otherwise a slow filesystem access is done. The cache values could be out of date if access was performed to the filesystem outside of this API.

:param update_db: If ``True`` The max_size field of the database is updated for this book. ''' if not fmt: return {} fmt = fmt.upper() # allow_cache and update_db are mutually exclusive. Give priority to update_db if allow_cache and not update_db: x = self.format_metadata_cache[book_id].get(fmt, None) if x is not None: return x with self.safe_read_lock: try: name = self.fields['formats'].format_fname(book_id, fmt) path = self._field_for('path', book_id).replace('/', os.sep) except: return {}

ans = {} if path and name: ans = self.backend.format_metadata(book_id, fmt, name, path) self.format_metadata_cache[book_id][fmt] = ans if update_db and 'size' in ans: with self.write_lock: max_size = self.fields['formats'].table.update_fmt(book_id, fmt, name, ans['size'], self.backend) self.fields['size'].table.update_sizes({book_id: max_size})

return ans@read_api def format_files(self, book_id): field = self.fields['formats'] fmts = field.table.book_col_map.get(book_id, ()) return {fmt:field.format_fname(book_id, fmt) for fmt in fmts}

@read_api def format_db_size(self, book_id, fmt): field = self.fields['formats'] return field.format_size(book_id, fmt)

[belgeler] @read_api def pref(self, name, default=None, namespace=None): ' Return the value for the specified preference or the value specified as ``default`` if the preference is not set. ' if namespace is not None: return self.backend.prefs.get_namespaced(namespace, name, default) return self.backend.prefs.get(name, default)

[belgeler] @write_api def set_pref(self, name, val, namespace=None): ' Set the specified preference to the specified value. See also :meth:`pref`. ' if namespace is not None: self.backend.prefs.set_namespaced(namespace, name, val) return self.backend.prefs.set(name, val) if name in ('grouped_search_terms', 'virtual_libraries'): self._clear_search_caches() if name in dynamic_category_preferences: self._initialize_dynamic_categories()

[belgeler] @api def get_metadata(self, book_id, get_cover=False, get_user_categories=True, cover_as_data=False): ''' Return metadata for the book identified by book_id as a :class:`calibre.ebooks.metadata.book.base.Metadata` object. Note that the list of formats is not verified. If get_cover is True, the cover is returned, either a path to temp file as mi.cover or if cover_as_data is True then as mi.cover_data. '''

# Check if virtual_libraries_for_books rebuilt its cache. If it did then # we must clear the composite caches so the new data can be taken into # account. Clearing the caches requires getting a write lock, so it must # be done outside of the closure of _get_metadata(). composite_cache_needs_to_be_cleared = False with self.safe_read_lock: vl_cache_was_none = self.vls_for_books_cache is None mi = self._get_metadata(book_id, get_user_categories=get_user_categories) if vl_cache_was_none and self.vls_for_books_cache is not None: composite_cache_needs_to_be_cleared = True if composite_cache_needs_to_be_cleared: try: self.clear_composite_caches() except LockingError: # We can't clear the composite caches because a read lock is set. # As a consequence the value of a composite column that calls # virtual_libraries() might be wrong. Oh well. Log and keep running. print('Couldn\'t get write lock after vls_for_books_cache was loaded', file=sys.stderr) traceback.print_exc()

if get_cover: if cover_as_data: cdata = self.cover(book_id) if cdata: mi.cover_data = ('jpeg', cdata) else: mi.cover = self.cover(book_id, as_path=True)

return mi

[belgeler] @read_api def get_proxy_metadata(self, book_id): ''' Like :meth:`get_metadata` except that it returns a ProxyMetadata object that only reads values from the database on demand. This is much faster than get_metadata when only a small number of fields need to be accessed from the returned metadata object. ''' return ProxyMetadata(self, book_id)

[belgeler] @api def cover(self, book_id, as_file=False, as_image=False, as_path=False, as_pixmap=False): ''' Return the cover image or None. By default, returns the cover as a bytestring.

WARNING: Using as_path will copy the cover to a temp file and return the path to the temp file. You should delete the temp file when you are done with it.

:param as_file: If True return the image as an open file object (a SpooledTemporaryFile) :param as_image: If True return the image as a QImage object :param as_pixmap: If True return the image as a QPixmap object :param as_path: If True return the image as a path pointing to a temporary file ''' if as_file: ret = SpooledTemporaryFile(SPOOL_SIZE) if not self.copy_cover_to(book_id, ret): ret.close() return ret.seek(0) elif as_path: pt = PersistentTemporaryFile('_dbcover.jpg') with pt: if not self.copy_cover_to(book_id, pt): return ret = pt.name elif as_pixmap or as_image: from qt.core import QImage, QPixmap ret = QImage() if as_image else QPixmap() with self.safe_read_lock: path = self._format_abspath(book_id, '__COVER_INTERNAL__') if path: ret.load(path) else: buf = BytesIO() if not self.copy_cover_to(book_id, buf): return ret = buf.getvalue() return ret

@read_api def cover_or_cache(self, book_id, timestamp, as_what='bytes'): try: path = self._field_for('path', book_id).replace('/', os.sep) except AttributeError: return False, None, None return self.backend.cover_or_cache(path, timestamp, as_what)

@read_api def cover_last_modified(self, book_id): try: path = self._field_for('path', book_id).replace('/', os.sep) except AttributeError: return return self.backend.cover_last_modified(path)

[belgeler] @read_api def copy_cover_to(self, book_id, dest, use_hardlink=False, report_file_size=None): ''' Copy the cover to the file like object ``dest``. Returns False if no cover exists or dest is the same file as the current cover. dest can also be a path in which case the cover is copied to it if and only if the path is different from the current path (taking case sensitivity into account). ''' try: path = self._field_for('path', book_id).replace('/', os.sep) except AttributeError: return False return self.backend.copy_cover_to(path, dest, use_hardlink=use_hardlink, report_file_size=report_file_size)

[belgeler] @write_api def compress_covers(self, book_ids, jpeg_quality=100, progress_callback=None): ''' Compress the cover images for the specified books. A compression quality of 100 will perform lossless compression, otherwise lossy compression.

The progress callback will be called with the book_id and the old and new sizes for each book that has been processed. If an error occurs, the new size will be a string with the error details. ''' jpeg_quality = max(10, min(jpeg_quality, 100)) path_map = {} for book_id in book_ids: try: path_map[book_id] = self._field_for('path', book_id).replace('/', os.sep) except AttributeError: continue self.backend.compress_covers(path_map, jpeg_quality, progress_callback)

[belgeler] @read_api def copy_format_to(self, book_id, fmt, dest, use_hardlink=False, report_file_size=None): ''' Copy the format ``fmt`` to the file like object ``dest``. If the specified format does not exist, raises :class:`NoSuchFormat` error. dest can also be a path (to a file), in which case the format is copied to it, iff the path is different from the current path (taking case sensitivity into account). ''' fmt = (fmt or '').upper() try: name = self.fields['formats'].format_fname(book_id, fmt) path = self._field_for('path', book_id).replace('/', os.sep) except (KeyError, AttributeError): raise NoSuchFormat('Record %d has no %s file'%(book_id, fmt))

return self.backend.copy_format_to(book_id, fmt, name, path, dest, use_hardlink=use_hardlink, report_file_size=report_file_size)

[belgeler] @read_api def format_abspath(self, book_id, fmt): ''' Return absolute path to the e-book file of format `format`. You should almost never use this, as it breaks the threadsafe promise of this API. Instead use, :meth:`copy_format_to`.

Currently used only in calibredb list, the viewer, edit book, compare_format to original format, open with, bulk metadata edit and the catalogs (via get_data_as_dict()).

Apart from the viewer, open with and edit book, I don't believe any of the others do any file write I/O with the results of this call. ''' fmt = (fmt or '').upper() try: path = self._field_for('path', book_id).replace('/', os.sep) except: return None if path: if fmt == '__COVER_INTERNAL__': return self.backend.cover_abspath(book_id, path) else: try: name = self.fields['formats'].format_fname(book_id, fmt) except: return None if name: return self.backend.format_abspath(book_id, fmt, name, path)

[belgeler] @read_api def has_format(self, book_id, fmt): 'Return True iff the format exists on disk' fmt = (fmt or '').upper() try: name = self.fields['formats'].format_fname(book_id, fmt) path = self._field_for('path', book_id).replace('/', os.sep) except: return False return self.backend.has_format(book_id, fmt, name, path)

[belgeler] @api def save_original_format(self, book_id, fmt): ' Save a copy of the specified format as ORIGINAL_FORMAT, overwriting any existing ORIGINAL_FORMAT. ' fmt = fmt.upper() if 'ORIGINAL' in fmt: raise ValueError('Cannot save original of an original fmt') fmtfile = self.format(book_id, fmt, as_file=True) if fmtfile is None: return False with fmtfile: nfmt = 'ORIGINAL_'+fmt return self.add_format(book_id, nfmt, fmtfile, run_hooks=False)

[belgeler] @write_api def restore_original_format(self, book_id, original_fmt): ''' Restore the specified format from the previously saved ORIGINAL_FORMAT, if any. Return True on success. The ORIGINAL_FORMAT is deleted after a successful restore. ''' original_fmt = original_fmt.upper() fmt = original_fmt.partition('_')[2] try: ofmt_name = self.fields['formats'].format_fname(book_id, original_fmt) path = self._field_for('path', book_id).replace('/', os.sep) except Exception: return False if self.backend.is_format_accessible(book_id, original_fmt, ofmt_name, path): self.add_format(book_id, fmt, BytesIO(), run_hooks=False) fmt_name = self.fields['formats'].format_fname(book_id, fmt) file_size = self.backend.rename_format_file(book_id, ofmt_name, original_fmt, fmt_name, fmt, path) self.fields['formats'].table.update_fmt(book_id, fmt, fmt_name, file_size, self.backend) self._remove_formats({book_id:(original_fmt,)}) return True return False

[belgeler] @read_api def formats(self, book_id, verify_formats=True): ''' Return tuple of all formats for the specified book. If verify_formats is True, verifies that the files exist on disk. ''' ans = self.field_for('formats', book_id) if verify_formats and ans: try: path = self._field_for('path', book_id).replace('/', os.sep) except: return ()

def verify(fmt): try: name = self.fields['formats'].format_fname(book_id, fmt) except: return False return self.backend.has_format(book_id, fmt, name, path)

ans = tuple(x for x in ans if verify(x)) return ans

[belgeler] @api def format(self, book_id, fmt, as_file=False, as_path=False, preserve_filename=False): ''' Return the e-book format as a bytestring or `None` if the format doesn't exist, or we don't have permission to write to the e-book file.

:param as_file: If True the e-book format is returned as a file object. Note that the file object is a SpooledTemporaryFile, so if what you want to do is copy the format to another file, use :meth:`copy_format_to` instead for performance. :param as_path: Copies the format file to a temp file and returns the path to the temp file :param preserve_filename: If True and returning a path the filename is the same as that used in the library. Note that using this means that repeated calls yield the same temp file (which is re-created each time) ''' fmt = (fmt or '').upper() ext = ('.'+fmt.lower()) if fmt else '' if as_path: if preserve_filename: with self.safe_read_lock: try: fname = self.fields['formats'].format_fname(book_id, fmt) except: return None fname += ext bd = base_dir() d = os.path.join(bd, 'format_abspath') try: os.makedirs(d) except: pass ret = os.path.join(d, fname) try: self.copy_format_to(book_id, fmt, ret) except NoSuchFormat: return None else: with PersistentTemporaryFile(ext) as pt: try: self.copy_format_to(book_id, fmt, pt) except NoSuchFormat: return None ret = pt.name elif as_file: with self.safe_read_lock: try: fname = self.fields['formats'].format_fname(book_id, fmt) except: return None fname += ext ret = SpooledTemporaryFile(SPOOL_SIZE) try: self.copy_format_to(book_id, fmt, ret) except NoSuchFormat: ret.close() return None ret.seek(0) # Various bits of code try to use the name as the default # title when reading metadata, so set it ret.name = fname else: buf = BytesIO() try: self.copy_format_to(book_id, fmt, buf) except NoSuchFormat: return None ret = buf.getvalue()

return ret@read_api def newly_added_book_ids(self, count=5, book_ids=None) -> list[int]: ids_to_sort = self._all_book_ids(list) if book_ids is None else list(book_ids) ids_to_sort.sort(reverse=True) return ids_to_sort[:count]

@read_api def size_stats(self) -> dict[str, int]: return self.backend.size_stats()

[belgeler] @read_api def multisort(self, fields, ids_to_sort=None, virtual_fields=None): ''' Return a list of sorted book ids. If ids_to_sort is None, all book ids are returned.

fields must be a list of 2-tuples of the form (field_name, ascending=True or False). The most significant field is the first 2-tuple. ''' ids_to_sort = self._all_book_ids() if ids_to_sort is None else ids_to_sort get_metadata = self._get_proxy_metadata lang_map = self.fields['languages'].book_value_map virtual_fields = virtual_fields or {}

fm = {'title':'sort', 'authors':'author_sort'}

def sort_key_func(field): 'Handle series type fields, virtual fields and the id field' idx = field + '_index' is_series = idx in self.fields try: func = self.fields[fm.get(field, field)].sort_keys_for_books(get_metadata, lang_map) except KeyError: if field == 'id': return IDENTITY else: return virtual_fields[fm.get(field, field)].sort_keys_for_books(get_metadata, lang_map) if is_series: idx_func = self.fields[idx].sort_keys_for_books(get_metadata, lang_map)

def skf(book_id): return (func(book_id), idx_func(book_id)) return skf return func

# Sort only once on any given field fields = uniq(fields, operator.itemgetter(0))

if len(fields) == 1: keyfunc = sort_key_func(fields[0][0]) reverse = not fields[0][1] try: return sorted(ids_to_sort, key=keyfunc, reverse=reverse) except Exception as err: print('Failed to sort database on field:', fields[0][0], 'with error:', err, file=sys.stderr) try: return sorted(ids_to_sort, key=type_safe_sort_key_function(keyfunc), reverse=reverse) except Exception as err: print('Failed to type-safe sort database on field:', fields[0][0], 'with error:', err, file=sys.stderr) return sorted(ids_to_sort, reverse=reverse) sort_key_funcs = tuple(sort_key_func(field) for field, order in fields) orders = tuple(1 if order else -1 for _, order in fields) Lazy = object() # Lazy load the sort keys for sub-sort fields class SortKey:

__slots__ = 'book_id', 'sort_key'

def __init__(self, book_id): self.book_id = book_id # Calculate only the first sub-sort key since that will always be used self.sort_key = [key(book_id) if i == 0 else Lazy for i, key in enumerate(sort_key_funcs)]

def compare_to_other(self, other): for i, (order, self_key, other_key) in enumerate(zip(orders, self.sort_key, other.sort_key)): if self_key is Lazy: self_key = self.sort_key[i] = sort_key_funcs[i](self.book_id) if other_key is Lazy: other_key = other.sort_key[i] = sort_key_funcs[i](other.book_id) ans = cmp(self_key, other_key) if ans != 0: return ans * order return 0 def __eq__(self, other): return self.compare_to_other(other) == 0 def __ne__(self, other): return self.compare_to_other(other) != 0 def __lt__(self, other): return self.compare_to_other(other) < 0 def __le__(self, other): return self.compare_to_other(other) <= 0 def __gt__(self, other): return self.compare_to_other(other) > 0 def __ge__(self, other): return self.compare_to_other(other) >= 0 return sorted(ids_to_sort, key=SortKey)

[belgeler] @read_api def search(self, query, restriction='', virtual_fields=None, book_ids=None): ''' Search the database for the specified query, returning a set of matched book ids.

:param restriction: A restriction that is ANDed to the specified query. Note that restrictions are cached, therefore the search for a AND b will be slower than a with restriction b.

:param virtual_fields: Used internally (virtual fields such as on_device to search over).

:param book_ids: If not None, a set of book ids for which books will be searched instead of searching all books. ''' return self._search_api(self, query, restriction, virtual_fields=virtual_fields, book_ids=book_ids)

[belgeler] @read_api def books_in_virtual_library(self, vl, search_restriction=None, virtual_fields=None): ' Return the set of books in the specified virtual library ' vl = self._pref('virtual_libraries', {}).get(vl) if vl else None if not vl and not search_restriction: return self.all_book_ids() # We utilize the search restriction cache to speed this up srch = partial(self._search, virtual_fields=virtual_fields) if vl: if search_restriction: return frozenset(srch('', vl) & srch('', search_restriction)) return frozenset(srch('', vl)) return frozenset(srch('', search_restriction))

@read_api def number_of_books_in_virtual_library(self, vl=None, search_restriction=None): if not vl and not search_restriction: return len(self.fields['uuid'].table.book_col_map) return len(self.books_in_virtual_library(vl, search_restriction))

[belgeler] @api def get_categories(self, sort='name', book_ids=None, already_fixed=None, first_letter_sort=False): ' Used internally to implement the Tag Browser ' try: with self.safe_read_lock: return get_categories(self, sort=sort, book_ids=book_ids, first_letter_sort=first_letter_sort) except InvalidLinkTable as err: bad_field = err.field_name if bad_field == already_fixed: raise with self.write_lock: self.fields[bad_field].table.fix_link_table(self.backend) return self.get_categories(sort=sort, book_ids=book_ids, already_fixed=bad_field)

@write_api def update_last_modified(self, book_ids, now=None): if book_ids: if now is None: now = nowf() f = self.fields['last_modified'] f.writer.set_books({book_id:now for book_id in book_ids}, self.backend) if self.composites: self._clear_composite_caches(book_ids) self._clear_search_caches(book_ids)

@write_api def mark_as_dirty(self, book_ids): self._update_last_modified(book_ids) already_dirtied = set(self.dirtied_cache).intersection(book_ids) new_dirtied = book_ids - already_dirtied already_dirtied = {book_id:self.dirtied_sequence+i for i, book_id in enumerate(already_dirtied)} if already_dirtied: self.dirtied_sequence = max(itervalues(already_dirtied)) + 1 self.dirtied_cache.update(already_dirtied) if new_dirtied: self.backend.dirty_books(new_dirtied) new_dirtied = {book_id:self.dirtied_sequence+i for i, book_id in enumerate(new_dirtied)} self.dirtied_sequence = max(itervalues(new_dirtied)) + 1 self.dirtied_cache.update(new_dirtied)

@write_api def commit_dirty_cache(self): if self.dirtied_cache: self.backend.dirty_books(self.dirtied_cache)

@write_api def check_dirtied_annotations(self): if not self.backend.dirty_books_with_dirtied_annotations(): return book_ids = set(self.backend.dirtied_books()) new_dirtied = book_ids - set(self.dirtied_cache) if new_dirtied: new_dirtied = {book_id:self.dirtied_sequence+i for i, book_id in enumerate(new_dirtied)} self.dirtied_sequence = max(itervalues(new_dirtied)) + 1 self.dirtied_cache.update(new_dirtied)

[belgeler] @write_api def set_field(self, name, book_id_to_val_map, allow_case_change=True, do_path_update=True): ''' Set the values of the field specified by ``name``. Returns the set of all book ids that were affected by the change.

:param book_id_to_val_map: Mapping of book_ids to values that should be applied. :param allow_case_change: If True, the case of many-one or many-many fields will be changed. For example, if a book has the tag ``tag1`` and you set the tag for another book to ``Tag1`` then the both books will have the tag ``Tag1`` if allow_case_change is True, otherwise they will both have the tag ``tag1``. :param do_path_update: Used internally, you should never change it. ''' f = self.fields[name] is_series = f.metadata['datatype'] == 'series' update_path = name in {'title', 'authors'} if update_path and iswindows: paths = (x for x in (self._field_for('path', book_id) for book_id in book_id_to_val_map) if x) self.backend.windows_check_if_files_in_use(paths)

if is_series: bimap, simap = {}, {} sfield = self.fields[name + '_index'] for k, v in iteritems(book_id_to_val_map): if isinstance(v, string_or_bytes): v, sid = get_series_values(v) else: v = sid = None if sid is None and name.startswith('#'): sid = self._fast_field_for(sfield, k) sid = 1.0 if sid is None else sid # The value to be set the db link table bimap[k] = v if sid is not None: simap[k] = sid book_id_to_val_map = bimap dirtied = f.writer.set_books( book_id_to_val_map, self.backend, allow_case_change=allow_case_change)

if is_series and simap: sf = self.fields[f.name+'_index'] dirtied |= sf.writer.set_books(simap, self.backend, allow_case_change=False)

if dirtied: if update_path and do_path_update: self._update_path(dirtied, mark_as_dirtied=False) self._mark_as_dirty(dirtied) self._clear_link_map_cache(dirtied) self.event_dispatcher(EventType.metadata_changed, name, dirtied) return dirtied@write_api def update_path(self, book_ids, mark_as_dirtied=True): for book_id in book_ids: title = self._field_for('title', book_id, default_value=_('Unknown')) try: author = self._field_for('authors', book_id, default_value=(_('Unknown'),))[0] except IndexError: author = _('Unknown') self.backend.update_path(book_id, title, author, self.fields['path'], self.fields['formats']) self.format_metadata_cache.pop(book_id, None) if mark_as_dirtied: self._mark_as_dirty(book_ids) self._clear_link_map_cache(book_ids)

@read_api def get_a_dirtied_book(self): if self.dirtied_cache: return random.choice(tuple(self.dirtied_cache)) return None def _metadata_as_object_for_dump(self, book_id): mi = self._get_metadata(book_id) # Always set cover to cover.jpg. Even if cover doesn't exist, # no harm done. This way no need to call dirtied when # cover is set/removed mi.cover = 'cover.jpg' mi.all_annotations = self._all_annotations_for_book(book_id) return mi

@read_api def get_metadata_for_dump(self, book_id): mi = None # get the current sequence number for this book to pass back to the # backup thread. This will avoid double calls in the case where the # thread has not done the work between the put and the get_metadata sequence = self.dirtied_cache.get(book_id, None) if sequence is not None: try: # While a book is being created, the path is empty. Don't bother to # try to write the opf, because it will go to the wrong folder. if self._field_for('path', book_id): mi = self._metadata_as_object_for_dump(book_id) except: # This almost certainly means that the book has been deleted while # the backup operation sat in the queue. traceback.print_exc() return mi, sequence

@write_api def clear_dirtied(self, book_id, sequence): # Clear the dirtied indicator for the books. This is used when fetching # metadata, creating an OPF, and writing a file are separated into steps. # The last step is clearing the indicator dc_sequence = self.dirtied_cache.get(book_id, None) if dc_sequence is None or sequence is None or dc_sequence == sequence: self.backend.mark_book_as_clean(book_id) self.dirtied_cache.pop(book_id, None)

@write_api def write_backup(self, book_id, raw): try: path = self._field_for('path', book_id).replace('/', os.sep) except: return self.backend.write_backup(path, raw)

@read_api def dirty_queue_length(self): return len(self.dirtied_cache)

[belgeler] @read_api def read_backup(self, book_id): ''' Return the OPF metadata backup for the book as a bytestring or None if no such backup exists. ''' try: path = self._field_for('path', book_id).replace('/', os.sep) except: return try: return self.backend.read_backup(path) except OSError: return None

@write_api def dump_metadata(self, book_ids=None, remove_from_dirtied=True, callback=None): # Write metadata for each record to an individual OPF file. If callback # is not None, it is called once at the start with the number of book_ids # being processed. And once for every book_id, with arguments (book_id, # mi, ok). if book_ids is None: book_ids = set(self.dirtied_cache)

if callback is not None: callback(len(book_ids), True, False)

for book_id in book_ids: if self._field_for('path', book_id) is None: if callback is not None: callback(book_id, None, False) continue mi, sequence = self._get_metadata_for_dump(book_id) if mi is None: if callback is not None: callback(book_id, mi, False) continue try: raw = metadata_to_opf(mi) self._write_backup(book_id, raw) if remove_from_dirtied: self._clear_dirtied(book_id, sequence) except: pass if callback is not None: callback(book_id, mi, True)

[belgeler] @write_api def set_cover(self, book_id_data_map): ''' Set the cover for this book. The data can be either a QImage, QPixmap, file object or bytestring. It can also be None, in which case any existing cover is removed. '''

for book_id, data in iteritems(book_id_data_map): try: path = self._field_for('path', book_id).replace('/', os.sep) except AttributeError: self._update_path((book_id,)) path = self._field_for('path', book_id).replace('/', os.sep)

self.backend.set_cover(book_id, path, data) for cc in self.cover_caches: cc.invalidate(book_id_data_map) return self._set_field('cover', { book_id:(0 if data is None else 1) for book_id, data in iteritems(book_id_data_map)})

@write_api def add_cover_cache(self, cover_cache): if not callable(cover_cache.invalidate): raise ValueError('Cover caches must have an invalidate method') self.cover_caches.add(cover_cache)

@write_api def remove_cover_cache(self, cover_cache): self.cover_caches.discard(cover_cache)

[belgeler] @write_api def set_metadata(self, book_id, mi, ignore_errors=False, force_changes=False, set_title=True, set_authors=True, allow_case_change=False): ''' Set metadata for the book `id` from the `Metadata` object `mi`

Setting force_changes=True will force set_metadata to update fields even if mi contains empty values. In this case, 'None' is distinguished from 'empty'. If mi.XXX is None, the XXX is not replaced, otherwise it is. The tags, identifiers, and cover attributes are special cases. Tags and identifiers cannot be set to None so they will always be replaced if force_changes is true. You must ensure that mi contains the values you want the book to have. Covers are always changed if a new cover is provided, but are never deleted. Also note that force_changes has no effect on setting title or authors. ''' dirtied = set()

try: # Handle code passing in an OPF object instead of a Metadata object mi = mi.to_book_metadata() except (AttributeError, TypeError): pass def set_field(name, val): dirtied.update(self._set_field(name, {book_id:val}, do_path_update=False, allow_case_change=allow_case_change))

path_changed = False if set_title and mi.title: path_changed = True set_field('title', mi.title) authors_changed = False if set_authors: path_changed = True if not mi.authors: mi.authors = [_('Unknown')] authors = [] for a in mi.authors: authors += string_to_authors(a) set_field('authors', authors) authors_changed = True if path_changed: self._update_path({book_id})

def protected_set_field(name, val): try: set_field(name, val) except: if ignore_errors: traceback.print_exc() else: raise

# force_changes has no effect on cover manipulation try: cdata = mi.cover_data[1] if cdata is None and isinstance(mi.cover, string_or_bytes) and mi.cover and os.access(mi.cover, os.R_OK): with open(mi.cover, 'rb') as f: cdata = f.read() or None if cdata is not None: self._set_cover({book_id: cdata}) except: if ignore_errors: traceback.print_exc() else: raise try: with self.backend.conn: # Speed up set_metadata by not operating in autocommit mode for field in ('rating', 'series_index', 'timestamp'): val = getattr(mi, field) if val is not None: protected_set_field(field, val)

val = mi.get('author_sort', None) if authors_changed and (not val or mi.is_null('author_sort')): val = self._author_sort_from_authors(mi.authors) if authors_changed or (force_changes and val is not None) or not mi.is_null('author_sort'): protected_set_field('author_sort', val)

for field in ('publisher', 'series', 'tags', 'comments', 'languages', 'pubdate'): val = mi.get(field, None) if (force_changes and val is not None) or not mi.is_null(field): protected_set_field(field, val)

val = mi.get('title_sort', None) if (force_changes and val is not None) or not mi.is_null('title_sort'): protected_set_field('sort', val)

# identifiers will always be replaced if force_changes is True mi_idents = mi.get_identifiers() if force_changes: protected_set_field('identifiers', mi_idents) elif mi_idents: identifiers = self._field_for('identifiers', book_id, default_value={}) for key, val in iteritems(mi_idents): if val and val.strip(): # Don't delete an existing identifier identifiers[icu_lower(key)] = val protected_set_field('identifiers', identifiers)

user_mi = mi.get_all_user_metadata(make_copy=False) fm = self.field_metadata for key in user_mi: if (key in fm and user_mi[key]['datatype'] == fm[key]['datatype'] and ( user_mi[key]['datatype'] != 'text' or ( user_mi[key]['is_multiple'] == fm[key]['is_multiple']))): val = mi.get(key, None) if force_changes or val is not None: protected_set_field(key, val) idx = key + '_index' if idx in self.fields: extra = mi.get_extra(key) if extra is not None or force_changes: protected_set_field(idx, extra) except: # sqlite will rollback the entire transaction, thanks to the with # statement, so we have to re-read everything form the db to ensure # the db and Cache are in sync self._reload_from_db() raise return dirtieddef _do_add_format(self, book_id, fmt, stream, name=None, mtime=None): path = self._field_for('path', book_id) if path is None: # Theoretically, this should never happen, but apparently it # does: https://www.mobileread.com/forums/showthread.php?t=233353 self._update_path({book_id}, mark_as_dirtied=False) path = self._field_for('path', book_id)

path = path.replace('/', os.sep) title = self._field_for('title', book_id, default_value=_('Unknown')) try: author = self._field_for('authors', book_id, default_value=(_('Unknown'),))[0] except IndexError: author = _('Unknown')

size, fname = self.backend.add_format(book_id, fmt, stream, title, author, path, name, mtime=mtime) return size, fname

[belgeler] @api def add_format(self, book_id, fmt, stream_or_path, replace=True, run_hooks=True, dbapi=None): ''' Add a format to the specified book. Return True if the format was added successfully.

:param replace: If True replace existing format, otherwise if the format already exists, return False. :param run_hooks: If True, file type plugins are run on the format before and after being added. :param dbapi: Internal use only. ''' needs_close = False if run_hooks: # Run import plugins, the write lock is not held to cater for # broken plugins that might spin the event loop by popping up a # message in the GUI during the processing. npath = run_import_plugins(stream_or_path, fmt) fmt = os.path.splitext(npath)[-1].lower().replace('.', '').upper() stream_or_path = open(make_long_path_useable(npath), 'rb') needs_close = True fmt = check_ebook_format(stream_or_path, fmt)

with self.write_lock: if not self._has_id(book_id): raise NoSuchBook(book_id) fmt = (fmt or '').upper() self.format_metadata_cache[book_id].pop(fmt, None) try: name = self.fields['formats'].format_fname(book_id, fmt) except Exception: name = None if name and not replace: if needs_close: stream_or_path.close() return False if hasattr(stream_or_path, 'read'): stream = stream_or_path else: stream = open(make_long_path_useable(stream_or_path), 'rb') needs_close = True try: size, fname = self._do_add_format(book_id, fmt, stream, name) finally: if needs_close: stream.close() del stream max_size = self.fields['formats'].table.update_fmt(book_id, fmt, fname, size, self.backend) self.fields['size'].table.update_sizes({book_id: max_size}) self._update_last_modified((book_id,)) self.event_dispatcher(EventType.format_added, book_id, fmt)

if run_hooks: # Run post import plugins, the write lock is released so the plugin # can call api without a locking violation. run_plugins_on_postimport(dbapi or self, book_id, fmt) stream_or_path.close()

self.queue_next_fts_job() return True[belgeler] @write_api def remove_formats(self, formats_map, db_only=False): ''' Remove the specified formats from the specified books.

:param formats_map: A mapping of book_id to a list of formats to be removed from the book. :param db_only: If True, only remove the record for the format from the db, do not delete the actual format file from the filesystem. :return: A map of book id to set of formats actually deleted from the filesystem for that book ''' table = self.fields['formats'].table formats_map = {book_id:frozenset((f or '').upper() for f in fmts) for book_id, fmts in iteritems(formats_map)} removed_map = {}

for book_id, fmts in iteritems(formats_map): for fmt in fmts: self.format_metadata_cache[book_id].pop(fmt, None)

if not db_only: removes = defaultdict(set) metadata_map = {} for book_id, fmts in iteritems(formats_map): try: path = self._field_for('path', book_id).replace('/', os.sep) except: continue for fmt in fmts: try: name = self.fields['formats'].format_fname(book_id, fmt) except: continue if name and path: removes[book_id].add((fmt, name, path)) if removes[book_id]: metadata_map[book_id] = {'title': self._field_for('title', book_id), 'authors': self._field_for('authors', book_id)} if removes: removed_map = self.backend.remove_formats(removes, metadata_map)

size_map = table.remove_formats(formats_map, self.backend) self.fields['size'].table.update_sizes(size_map)

for book_id, fmts in iteritems(formats_map): for fmt in fmts: run_plugins_on_postdelete(self, book_id, fmt)

self._update_last_modified(tuple(formats_map)) self.event_dispatcher(EventType.formats_removed, formats_map) return removed_map

[belgeler] @read_api def get_next_series_num_for(self, series, field='series', current_indices=False): ''' Return the next series index for the specified series, taking into account the various preferences that control next series number generation.

:param field: The series-like field (defaults to the builtin series column) :param current_indices: If True, returns a mapping of book_id to current series_index value instead. ''' books = () sf = self.fields[field] if series: q = icu_lower(series) for val, book_ids in sf.iter_searchable_values(self._get_proxy_metadata, frozenset(self._all_book_ids())): if q == icu_lower(val): books = book_ids break idf = sf.index_field index_map = {book_id:self._fast_field_for(idf, book_id, default_value=1.0) for book_id in books} if current_indices: return index_map series_indices = sorted(index_map.values(), key=lambda s: s or 0) return _get_next_series_num_for_list(tuple(series_indices), unwrap=False)

[belgeler] @read_api def author_sort_from_authors(self, authors, key_func=icu_lower): '''Given a list of authors, return the author_sort string for the authors, preferring the author sort associated with the author over the computed string. ''' table = self.fields['authors'].table result = [] rmap = {key_func(v):k for k, v in iteritems(table.id_map)} for aut in authors: aid = rmap.get(key_func(aut), None) result.append(author_to_author_sort(aut) if aid is None else table.asort_map[aid]) return ' & '.join(_f for _f in result if _f)

[belgeler] @read_api def data_for_has_book(self): ''' Return data suitable for use in :meth:`has_book`. This can be used for an implementation of :meth:`has_book` in a worker process without access to the db. ''' try: return {icu_lower(title) for title in itervalues(self.fields['title'].table.book_col_map)} except TypeError: # Some non-unicode titles in the db return {icu_lower(as_unicode(title)) for title in itervalues(self.fields['title'].table.book_col_map)}

[belgeler] @read_api def has_book(self, mi): ''' Return True iff the database contains an entry with the same title as the passed in Metadata object. The comparison is case-insensitive. See also :meth:`data_for_has_book`. ''' title = mi.title if title: if isbytestring(title): title = title.decode(preferred_encoding, 'replace') q = icu_lower(title).strip() for title in itervalues(self.fields['title'].table.book_col_map): if q == icu_lower(title): return True return False

[belgeler] @read_api def has_id(self, book_id): ' Return True iff the specified book_id exists in the db ''' return book_id in self.fields['title'].table.book_col_map

@write_api def create_book_entry(self, mi, cover=None, add_duplicates=True, force_id=None, apply_import_tags=True, preserve_uuid=False): if mi.tags: mi.tags = list(mi.tags) if apply_import_tags: _add_newbook_tag(mi) _add_default_custom_column_values(mi, self.field_metadata) if not add_duplicates and self._has_book(mi): return series_index = (self._get_next_series_num_for(mi.series) if mi.series_index is None else mi.series_index) try: series_index = float(series_index) except Exception: try: series_index = float(self._get_next_series_num_for(mi.series)) except Exception: series_index = 1.0 if not mi.authors: mi.authors = (_('Unknown'),) aus = mi.author_sort if not mi.is_null('author_sort') else self._author_sort_from_authors(mi.authors) mi.title = mi.title or _('Unknown') if isbytestring(aus): aus = aus.decode(preferred_encoding, 'replace') if isbytestring(mi.title): mi.title = mi.title.decode(preferred_encoding, 'replace') if force_id is None: self.backend.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)', (mi.title, series_index, aus)) else: self.backend.execute('INSERT INTO books(id, title, series_index, author_sort) VALUES (?, ?, ?, ?)', (force_id, mi.title, series_index, aus)) book_id = self.backend.last_insert_rowid() self.event_dispatcher(EventType.book_created, book_id)

mi.timestamp = utcnow() if mi.timestamp is None else mi.timestamp mi.pubdate = UNDEFINED_DATE if mi.pubdate is None else mi.pubdate if cover is not None: mi.cover, mi.cover_data = None, (None, cover) self._set_metadata(book_id, mi, ignore_errors=True) lm = getattr(mi, 'link_maps', None) if lm: for field, link_map in lm.items(): if self._has_link_map(field): self._set_link_map(field, link_map, only_set_if_no_existing_link=True) if preserve_uuid and mi.uuid: self._set_field('uuid', {book_id:mi.uuid}) # Update the caches for fields from the books table self.fields['size'].table.book_col_map[book_id] = 0 row = next(self.backend.execute('SELECT sort, series_index, author_sort, uuid, has_cover FROM books WHERE id=?', (book_id,))) for field, val in zip(('sort', 'series_index', 'author_sort', 'uuid', 'cover'), row): if field == 'cover': val = bool(val) elif field == 'uuid': self.fields[field].table.uuid_to_id_map[val] = book_id self.fields[field].table.book_col_map[book_id] = val return book_id

[belgeler] @api def add_books(self, books, add_duplicates=True, apply_import_tags=True, preserve_uuid=False, run_hooks=True, dbapi=None): ''' Add the specified books to the library. Books should be an iterable of 2-tuples, each 2-tuple of the form :code:`(mi, format_map)` where mi is a Metadata object and format_map is a dictionary of the form :code:`{fmt: path_or_stream}`, for example: :code:`{'EPUB': '/path/to/file.epub'}`.

Returns a pair of lists: :code:`ids, duplicates`. ``ids`` contains the book ids for all newly created books in the database. ``duplicates`` contains the :code:`(mi, format_map)` for all books that already exist in the database as per the simple duplicate detection heuristic used by :meth:`has_book`. ''' duplicates, ids = [], [] for mi, format_map in books: book_id = self.create_book_entry(mi, add_duplicates=add_duplicates, apply_import_tags=apply_import_tags, preserve_uuid=preserve_uuid) if book_id is None: duplicates.append((mi, format_map)) else: fmt_map = {} ids.append(book_id) for fmt, stream_or_path in format_map.items(): if self.add_format(book_id, fmt, stream_or_path, dbapi=dbapi, run_hooks=run_hooks): fmt_map[fmt.lower()] = getattr(stream_or_path, 'name', stream_or_path) or '<stream>' run_plugins_on_postadd(dbapi or self, book_id, fmt_map) return ids, duplicates

[belgeler] @write_api def remove_books(self, book_ids, permanent=False): ''' Remove the books specified by the book_ids from the database and delete their format files. If ``permanent`` is False, then the format files are placed in the per-library trash directory. ''' path_map = {} for book_id in book_ids: try: path = self._field_for('path', book_id).replace('/', os.sep) except Exception: path = None path_map[book_id] = path if not permanent and path: # ensure metadata.opf is written and up-to-date so we can restore the book try: mi = self._metadata_as_object_for_dump(book_id) raw = metadata_to_opf(mi) self.backend.write_backup(path, raw) except Exception: traceback.print_exc() self.backend.remove_books(path_map, permanent=permanent) for field in itervalues(self.fields): try: table = field.table except AttributeError: continue # Some fields like ondevice do not have tables else: table.remove_books(book_ids, self.backend) self._search_api.discard_books(book_ids) self._clear_caches(book_ids=book_ids, template_cache=False, search_cache=False) for cc in self.cover_caches: cc.invalidate(book_ids) self.event_dispatcher(EventType.books_removed, book_ids)@read_api def author_sort_strings_for_books(self, book_ids): val_map = {} for book_id in book_ids: authors = self._field_ids_for('authors', book_id) adata = self._author_data(authors) val_map[book_id] = tuple(adata[aid]['sort'] for aid in authors) return val_map

[belgeler] @write_api def rename_items(self, field, item_id_to_new_name_map, change_index=True, restrict_to_book_ids=None): ''' Rename items from a many-one or many-many field such as tags or series.

:param change_index: When renaming in a series-like field also change the series_index values. :param restrict_to_book_ids: An optional set of book ids for which the rename is to be performed, defaults to all books. '''

f = self.fields[field] affected_books = set() try: sv = f.metadata['is_multiple']['ui_to_list'] except (TypeError, KeyError, AttributeError): sv = None if restrict_to_book_ids is not None: # We have a VL. Only change the item name for those books if not isinstance(restrict_to_book_ids, (Set, MutableSet)): restrict_to_book_ids = frozenset(restrict_to_book_ids) id_map = {} default_process_map = {} for old_id, new_name in iteritems(item_id_to_new_name_map): new_names = tuple(x.strip() for x in new_name.split(sv)) if sv else (new_name,) # Get a list of books in the VL with the item books_with_id = f.books_for(old_id) books_to_process = books_with_id & restrict_to_book_ids if len(books_with_id) == len(books_to_process): # All the books with the ID are in the VL, so we can use # the normal processing default_process_map[old_id] = new_name elif books_to_process: affected_books.update(books_to_process) newvals = {} for book_id in books_to_process: # Get the current values, remove the one being renamed, then add # the new value(s) back. vals = self._field_for(field, book_id) # Check for is_multiple if isinstance(vals, tuple): # We must preserve order. vals = list(vals) # Don't need to worry about case here because we # are fetching its one-true spelling. But lets be # careful anyway try: dex = vals.index(self._get_item_name(field, old_id)) # This can put the name back with a different case vals[dex] = new_names[0] # now add any other items if they aren't already there if len(new_names) > 1: set_vals = {icu_lower(x) for x in vals} for v in new_names[1:]: lv = icu_lower(v) if lv not in set_vals: vals.append(v) set_vals.add(lv) newvals[book_id] = vals except Exception: traceback.print_exc() else: newvals[book_id] = new_names[0] # Allow case changes self._set_field(field, newvals) id_map[old_id] = self._get_item_id(field, new_names[0]) if default_process_map: ab, idm = self._rename_items(field, default_process_map, change_index=change_index) affected_books.update(ab) id_map.update(idm) self.event_dispatcher(EventType.items_renamed, field, affected_books, id_map) return affected_books, id_map try: func = f.table.rename_item except AttributeError: raise ValueError('Cannot rename items for one-one fields: %s' % field) moved_books = set() id_map = {} for item_id, new_name in iteritems(item_id_to_new_name_map): new_names = tuple(x.strip() for x in new_name.split(sv)) if sv else (new_name,) books, new_id = func(item_id, new_names[0], self.backend) affected_books.update(books) id_map[item_id] = new_id if new_id != item_id: moved_books.update(books) if len(new_names) > 1: # Add the extra items to the books extra = new_names[1:] self._set_field(field, {book_id:self._fast_field_for(f, book_id) + extra for book_id in books})

if affected_books: if field == 'authors': self._set_field('author_sort', {k:' & '.join(v) for k, v in iteritems(self._author_sort_strings_for_books(affected_books))}) self._update_path(affected_books, mark_as_dirtied=False) elif change_index and hasattr(f, 'index_field') and tweaks['series_index_auto_increment'] != 'no_change': for book_id in moved_books: self._set_field(f.index_field.name, {book_id:self._get_next_series_num_for(self._fast_field_for(f, book_id), field=field)}) self._mark_as_dirty(affected_books) self._clear_link_map_cache(affected_books) self.event_dispatcher(EventType.items_renamed, field, affected_books, id_map) return affected_books, id_map

[belgeler] @write_api def remove_items(self, field, item_ids, restrict_to_book_ids=None): ''' Delete all items in the specified field with the specified ids. Returns the set of affected book ids. ``restrict_to_book_ids`` is an optional set of books ids. If specified the items will only be removed from those books. ''' field = self.fields[field] if restrict_to_book_ids is not None and not isinstance(restrict_to_book_ids, (MutableSet, Set)): restrict_to_book_ids = frozenset(restrict_to_book_ids) affected_books = field.table.remove_items(item_ids, self.backend, restrict_to_book_ids=restrict_to_book_ids) if affected_books: if hasattr(field, 'index_field'): self._set_field(field.index_field.name, {bid:1.0 for bid in affected_books}) else: self._mark_as_dirty(affected_books) self._clear_link_map_cache(affected_books) self.event_dispatcher(EventType.items_removed, field, affected_books, item_ids) return affected_books

[belgeler] @write_api def add_custom_book_data(self, name, val_map, delete_first=False): ''' Add data for name where val_map is a map of book_ids to values. If delete_first is True, all previously stored data for name will be removed. ''' missing = frozenset(val_map) - self._all_book_ids() if missing: raise ValueError('add_custom_book_data: no such book_ids: %d'%missing) self.backend.add_custom_data(name, val_map, delete_first)

[belgeler] @read_api def get_custom_book_data(self, name, book_ids=(), default=None): ''' Get data for name. By default returns data for all book_ids, pass in a list of book ids if you only want some data. Returns a map of book_id to values. If a particular value could not be decoded, uses default for it. ''' return self.backend.get_custom_book_data(name, book_ids, default)

[belgeler] @write_api def delete_custom_book_data(self, name, book_ids=()): ''' Delete data for name. By default deletes all data, if you only want to delete data for some book ids, pass in a list of book ids. ''' self.backend.delete_custom_book_data(name, book_ids)

[belgeler] @read_api def get_ids_for_custom_book_data(self, name): ''' Return the set of book ids for which name has data. ''' return self.backend.get_ids_for_custom_book_data(name)

@read_api def conversion_options(self, book_id, fmt='PIPE'): return self.backend.conversion_options(book_id, fmt)

@read_api def has_conversion_options(self, ids, fmt='PIPE'): return self.backend.has_conversion_options(ids, fmt)

@write_api def delete_conversion_options(self, book_ids, fmt='PIPE'): return self.backend.delete_conversion_options(book_ids, fmt)

[belgeler] @write_api def set_conversion_options(self, options, fmt='PIPE'): ''' options must be a map of the form {book_id:conversion_options} ''' return self.backend.set_conversion_options(options, fmt)

@write_api def refresh_format_cache(self): self.fields['formats'].table.read(self.backend) self.format_metadata_cache.clear()

@write_api def refresh_ondevice(self): self.fields['ondevice'].clear_caches() self.clear_search_caches() self.clear_composite_caches()

@read_api def books_matching_device_book(self, lpath): ans = set() for book_id, (_, _, _, _, lpaths) in self.fields['ondevice'].cache.items(): if lpath in lpaths: ans.add(book_id) return ans

[belgeler] @read_api def tags_older_than(self, tag, delta=None, must_have_tag=None, must_have_authors=None): ''' Return the ids of all books having the tag ``tag`` that are older than the specified time. tag comparison is case insensitive.

:param delta: A timedelta object or None. If None, then all ids with the tag are returned.

:param must_have_tag: If not None the list of matches will be restricted to books that have this tag

:param must_have_authors: A list of authors. If not None the list of matches will be restricted to books that have these authors (case insensitive).

''' tag_map = {icu_lower(v):k for k, v in iteritems(self._get_id_map('tags'))} tag = icu_lower(tag.strip()) mht = icu_lower(must_have_tag.strip()) if must_have_tag else None tag_id, mht_id = tag_map.get(tag, None), tag_map.get(mht, None) ans = set() if mht_id is None and mht: return ans if tag_id is not None: tagged_books = self._books_for_field('tags', tag_id) if mht_id is not None and tagged_books: tagged_books = tagged_books.intersection(self._books_for_field('tags', mht_id)) if tagged_books: if must_have_authors is not None: amap = {icu_lower(v):k for k, v in iteritems(self._get_id_map('authors'))} books = None for author in must_have_authors: abooks = self._books_for_field('authors', amap.get(icu_lower(author), None)) books = abooks if books is None else books.intersection(abooks) if not books: break tagged_books = tagged_books.intersection(books or set()) if delta is None: ans = tagged_books else: now = nowf() for book_id in tagged_books: ts = self._field_for('timestamp', book_id) if (now - ts) > delta: ans.add(book_id) return ans@write_api def set_sort_for_authors(self, author_id_to_sort_map, update_books=True): sort_map = self.fields['authors'].table.set_sort_names(author_id_to_sort_map, self.backend) changed_books = set() if update_books: val_map = {} for author_id in sort_map: books = self._books_for_field('authors', author_id) changed_books |= books for book_id in books: authors = self._field_ids_for('authors', book_id) adata = self._author_data(authors) sorts = [adata[x]['sort'] for x in authors] val_map[book_id] = ' & '.join(sorts) if val_map: self._set_field('author_sort', val_map) if changed_books: self._mark_as_dirty(changed_books) self._clear_link_map_cache(changed_books) return changed_books

@write_api def set_link_for_authors(self, author_id_to_link_map): link_map = self.fields['authors'].table.set_links(author_id_to_link_map, self.backend) changed_books = set() for author_id in link_map: changed_books |= self._books_for_field('authors', author_id) if changed_books: self._mark_as_dirty(changed_books) self._clear_link_map_cache(changed_books) return changed_books

@read_api def has_link_map(self, field): return hasattr(getattr(self.fields.get(field), 'table', None), 'link_map')

[belgeler] @read_api def get_link_map(self, for_field): ''' Return a dictionary of links for the supplied field.

:param for_field: the lookup name of the field for which the link map is desired

:return: {field_value:link_value, ...} for non-empty links ''' if for_field not in self.fields: raise ValueError(f'Lookup name {for_field} is not a valid name') table = self.fields[for_field].table lm = getattr(table, 'link_map', None) if lm is None: raise ValueError(f"Lookup name {for_field} doesn't have a link map") lm = table.link_map vm = table.id_map return {vm.get(fid):v for fid,v in lm.items() if v}

[belgeler] @read_api def link_for(self, field, item_id): ''' Return the link, if any, for the specified item or None if no link is found ''' f = self.fields.get(field) if f is not None: table = f.table lm = getattr(table, 'link_map', None) if lm is not None: return lm.get(item_id)

[belgeler] @read_api def get_all_link_maps_for_book(self, book_id): ''' Returns all links for all fields referenced by book identified by book_id. If book_id doesn't exist then the method returns {}.

Example: Assume author A has link X, author B has link Y, tag S has link F, and tag T has link G. If book 1 has author A and tag T, this method returns {'authors':{'A':'X'}, 'tags':{'T', 'G'}}. If book 2's author is neither A nor B and has no tags, this method returns {}.

:param book_id: the book id in question.

:return: {field: {field_value, link_value}, ... for all fields with a field_value having a non-empty link value for that book

''' if not self._has_id(book_id): # Works for book_id is None. return {} cached = self.link_maps_cache.get(book_id) if cached is not None: return cached links = {} def add_links_for_field(f): field_ids = self._field_ids_for(f, book_id) if field_ids: table = self.fields[f].table lm = table.link_map id_link_map = {fid:lm.get(fid) for fid in field_ids} vm = table.id_map d = {vm.get(fid):v for fid, v in id_link_map.items() if v} d.pop(None, None) if d: links[f] = d for field in ('authors', 'publisher', 'series', 'tags'): add_links_for_field(field) for field in self.field_metadata.custom_field_keys(include_composites=False): if self._has_link_map(field): add_links_for_field(field) self.link_maps_cache[book_id] = links return links[belgeler] @write_api def set_link_map(self, field, value_to_link_map, only_set_if_no_existing_link=False): ''' Sets links for item values in field. Note: this method doesn't change values not in the value_to_link_map

:param field: the lookup name :param value_to_link_map: dict(field_value:link, ...). Note that these are values, not field ids.

:return: books changed by setting the link

''' if field not in self.fields: raise ValueError(f'Lookup name {field} is not a valid name') table = getattr(self.fields[field], 'table', None) if table is None: raise ValueError(f"Lookup name {field} doesn't have a link map") # Clear the links for book cache as we don't know what will be affected self.link_maps_cache = {}

fids = self._get_item_ids(field, value_to_link_map) if only_set_if_no_existing_link: lm = table.link_map id_to_link_map = {fid:value_to_link_map[k] for k, fid in fids.items() if fid is not None and not lm.get(fid)} else: id_to_link_map = {fid:value_to_link_map[k] for k, fid in fids.items() if fid is not None} result_map = table.set_links(id_to_link_map, self.backend) changed_books = set() for id_ in result_map: changed_books |= self._books_for_field(field, id_) if changed_books: self._mark_as_dirty(changed_books) self._clear_link_map_cache(changed_books) return changed_books@read_api def lookup_by_uuid(self, uuid): return self.fields['uuid'].table.lookup_by_uuid(uuid)

@write_api def delete_custom_column(self, label=None, num=None): self.backend.delete_custom_column(label, num)

@write_api def create_custom_column(self, label, name, datatype, is_multiple, editable=True, display={}): return self.backend.create_custom_column(label, name, datatype, is_multiple, editable=editable, display=display)

@write_api def set_custom_column_metadata(self, num, name=None, label=None, is_editable=None, display=None, update_last_modified=False): changed = self.backend.set_custom_column_metadata(num, name=name, label=label, is_editable=is_editable, display=display) if changed: if update_last_modified: self._update_last_modified(self._all_book_ids()) else: self.backend.prefs.set('update_all_last_mod_dates_on_start', True) return changed

@read_api def get_books_for_category(self, category, item_id_or_composite_value): f = self.fields[category] if hasattr(f, 'get_books_for_val'): # Composite field return f.get_books_for_val(item_id_or_composite_value, self._get_proxy_metadata, self._all_book_ids()) return self._books_for_field(f.name, int(item_id_or_composite_value))

[belgeler] @read_api def split_if_is_multiple_composite(self, f, val): ''' If f is a composite column lookup key and the column is is_multiple then split v into unique non-empty values. The comparison is case sensitive. Order is not preserved. Return a list() for compatibility with proxy metadata field getters, for example tags. ''' fm = self.field_metadata.get(f, None) if fm and fm['datatype'] == 'composite' and fm['is_multiple']: sep = fm['is_multiple'].get('cache_to_list', ',') return list({v.strip() for v in val.split(sep) if v.strip()}) return val

[belgeler] @read_api def data_for_find_identical_books(self): ''' Return data that can be used to implement :meth:`find_identical_books` in a worker process without access to the db. See db.utils for an implementation. ''' at = self.fields['authors'].table author_map = defaultdict(set) for aid, author in iteritems(at.id_map): author_map[icu_lower(author)].add(aid) return (author_map, at.col_book_map.copy(), self.fields['title'].table.book_col_map.copy(), self.fields['languages'].book_value_map.copy())

@read_api def update_data_for_find_identical_books(self, book_id, data): author_map, author_book_map, title_map, lang_map = data title_map[book_id] = self._field_for('title', book_id) lang_map[book_id] = self._field_for('languages', book_id) at = self.fields['authors'].table for aid in at.book_col_map.get(book_id, ()): author_map[icu_lower(at.id_map[aid])].add(aid) try: author_book_map[aid].add(book_id) except KeyError: author_book_map[aid] = {book_id}

[belgeler] @read_api def find_identical_books(self, mi, search_restriction='', book_ids=None): ''' Finds books that have a superset of the authors in mi and the same title (title is fuzzy matched). See also :meth:`data_for_find_identical_books`. ''' from calibre.db.utils import fuzzy_title identical_book_ids = set() langq = tuple(x for x in map(canonicalize_lang, mi.languages or ()) if x and x != 'und') if mi.authors: try: quathors = mi.authors[:20] # Too many authors causes parsing of the search expression to fail query = ' and '.join('authors:"=%s"'%(a.replace('"', '')) for a in quathors) qauthors = mi.authors[20:] except ValueError: return identical_book_ids try: book_ids = self._search(query, restriction=search_restriction, book_ids=book_ids) except: traceback.print_exc() return identical_book_ids if qauthors and book_ids: matches = set() qauthors = {icu_lower(x) for x in qauthors} for book_id in book_ids: aut = self._field_for('authors', book_id) if aut: aut = {icu_lower(x) for x in aut} if aut.issuperset(qauthors): matches.add(book_id) book_ids = matches for book_id in book_ids: fbook_title = self._field_for('title', book_id) fbook_title = fuzzy_title(fbook_title) mbook_title = fuzzy_title(mi.title) if fbook_title == mbook_title: bl = self._field_for('languages', book_id) if not langq or not bl or bl == langq: identical_book_ids.add(book_id) return identical_book_ids@read_api def get_top_level_move_items(self): all_paths = {self._field_for('path', book_id).partition('/')[0] for book_id in self._all_book_ids()} return self.backend.get_top_level_move_items(all_paths)

@write_api def move_library_to(self, newloc, progress=None, abort=None): def progress_callback(item_name, item_count, total): try: if progress is not None: progress(item_name, item_count, total) except Exception: traceback.print_exc()

all_paths = {self._field_for('path', book_id).partition('/')[0] for book_id in self._all_book_ids()} self.backend.move_library_to(all_paths, newloc, progress=progress_callback, abort=abort)

@read_api def saved_search_names(self): return self._search_api.saved_searches.names()

@read_api def saved_search_lookup(self, name): return self._search_api.saved_searches.lookup(name)

@write_api def saved_search_set_all(self, smap): self._search_api.saved_searches.set_all(smap) self._clear_search_caches()

@write_api def saved_search_delete(self, name): self._search_api.saved_searches.delete(name) self._clear_search_caches()

@write_api def saved_search_add(self, name, val): self._search_api.saved_searches.add(name, val)

@write_api def saved_search_rename(self, old_name, new_name): self._search_api.saved_searches.rename(old_name, new_name) self._clear_search_caches()

@write_api def change_search_locations(self, newlocs): self._search_api.change_locations(newlocs)

@write_api def refresh_search_locations(self): self._search_api.change_locations(self.field_metadata.get_search_terms())

@write_api def dump_and_restore(self, callback=None, sql=None): return self.backend.dump_and_restore(callback=callback, sql=sql)

@write_api def vacuum(self, include_fts_db=False, include_notes_db=True): self.is_doing_rebuild_or_vacuum = True try: self.backend.vacuum(include_fts_db, include_notes_db) finally: self.is_doing_rebuild_or_vacuum = False def __del__(self): self.close()

def _shutdown_fts(self, stage=1): if stage == 1: self.backend.shutdown_fts() if self.fts_queue_thread is not None: self.fts_job_queue.put(None) if hasattr(self, 'fts_dispatch_stop_event'): self.fts_dispatch_stop_event.set() return # the fts supervisor thread could be in the middle of committing a # result to the db, so holding a lock here will cause a deadlock if self.fts_queue_thread is not None: self.fts_queue_thread.join() self.fts_queue_thread = None self.backend.join_fts()

@api def close(self): with self.write_lock: if hasattr(self, 'close_called'): return self.close_called = True self.shutting_down = True self.event_dispatcher.close() self._shutdown_fts() try: from calibre.customize.ui import available_library_closed_plugins except ImportError: pass # happens during interpreter shutdown else: for plugin in available_library_closed_plugins(): try: plugin.run(self) except Exception: traceback.print_exc() self._shutdown_fts(stage=2) with self.write_lock: self.backend.close()

@property def is_closed(self): return self.backend.is_closed

@write_api def clear_trash_bin(self): self.backend.clear_trash_dir()

@read_api def list_trash_entries(self): books, formats = self.backend.list_trash_entries() ff = [] for e in formats: if self._has_id(e.book_id): ff.append(e) e.cover_path = self.format_abspath(e.book_id, '__COVER_INTERNAL__') return books, formats

@read_api def copy_format_from_trash(self, book_id, fmt, dest): fmt = fmt.upper() fpath = self.backend.path_for_trash_format(book_id, fmt) if not fpath: raise ValueError(f'No format {fmt} found in book {book_id}') shutil.copyfile(fpath, dest)

[belgeler] @write_api def move_format_from_trash(self, book_id, fmt): ''' Undelete a format from the trash directory ''' if not self._has_id(book_id): raise ValueError(f'A book with the id {book_id} does not exist') fmt = fmt.upper() try: name = self.fields['formats'].format_fname(book_id, fmt) except Exception: name = None fpath = self.backend.path_for_trash_format(book_id, fmt) if not fpath: raise ValueError(f'No format {fmt} found in book {book_id}') size, fname = self._do_add_format(book_id, fmt, fpath, name) self.format_metadata_cache.pop(book_id, None) max_size = self.fields['formats'].table.update_fmt(book_id, fmt, fname, size, self.backend) self.fields['size'].table.update_sizes({book_id: max_size}) self.event_dispatcher(EventType.format_added, book_id, fmt) self.backend.remove_trash_formats_dir_if_empty(book_id)

@read_api def copy_book_from_trash(self, book_id, dest: str): self.backend.copy_book_from_trash(book_id, dest)

[belgeler] @write_api def move_book_from_trash(self, book_id): ''' Undelete a book from the trash directory ''' if self._has_id(book_id): raise ValueError(f'A book with the id {book_id} already exists') mi, annotations, formats = self.backend.get_metadata_for_trash_book(book_id) mi.cover = None self._create_book_entry(mi, add_duplicates=True, force_id=book_id, apply_import_tags=False, preserve_uuid=True) path = self._field_for('path', book_id).replace('/', os.sep) self.backend.move_book_from_trash(book_id, path) self.format_metadata_cache.pop(book_id, None) f = self.fields['formats'].table max_size = 0 for (fmt, size, fname) in formats: max_size = max(max_size, f.update_fmt(book_id, fmt, fname, size, self.backend)) self.fields['size'].table.update_sizes({book_id: max_size}) cover = self.backend.cover_abspath(book_id, path) if cover and os.path.exists(cover): self._set_field('cover', {book_id:1}) if annotations: self._restore_annotations(book_id, annotations)

[belgeler] @write_api def delete_trash_entry(self, book_id, category): " Delete an entry from the trash. Here category is 'b' for books and 'f' for formats. " self.backend.delete_trash_entry(book_id, category)

[belgeler] @write_api def expire_old_trash(self): ' Expire entries from the trash that are too old ' self.backend.expire_old_trash()

[belgeler] @write_api def restore_book(self, book_id, mi, last_modified, path, formats, annotations=()): ''' Restore the book entry in the database for a book that already exists on the filesystem ''' cover, mi.cover = mi.cover, None self._create_book_entry(mi, add_duplicates=True, force_id=book_id, apply_import_tags=False, preserve_uuid=True) self._update_last_modified((book_id,), last_modified) if cover and os.path.exists(cover): self._set_field('cover', {book_id:1}) f = self.fields['formats'].table for (fmt, size, fname) in formats: f.update_fmt(book_id, fmt, fname, size, self.backend) self.fields['path'].table.set_path(book_id, path, self.backend) if annotations: self._restore_annotations(book_id, annotations)

@read_api def virtual_libraries_for_books(self, book_ids, virtual_fields=None): # use a primitive lock to ensure that only one thread is updating # the cache and that recursive calls don't do the update. This # method can recurse via self._search() with try_lock(self.vls_cache_lock) as got_lock: # Using a list is slightly faster than a set. c = defaultdict(list) if not got_lock: # We get here if resolving the books in a VL triggers another VL # cache calculation. This can be 'real' recursion, for example a # VL expression using a template that calls virtual_libraries(), # or a search using a location of 'all' that causes evaluation # of a composite that uses virtual_libraries(). The first case # is an error and the exception message should appear somewhere. # However, the error can seem nondeterministic. It might not be # raised if the use is via a composite and that composite is # evaluated before it is used in the search. The second case is # also an error but if the composite isn't used in a VL then the # eventual answer will be correct because get_metadata() will # clear the caches. raise ValueError(_('Recursion detected while processing Virtual library "%s"') % self.vls_for_books_lib_in_process) if self.vls_for_books_cache is None: libraries = self._pref('virtual_libraries', {}) for lib, expr in libraries.items(): book = None self.vls_for_books_lib_in_process = lib try: for book in self._search(expr, virtual_fields=virtual_fields): c[book].append(lib) except Exception as e: if book: c[book].append(_('[Error in Virtual library {0}: {1}]').format(lib, str(e))) self.vls_for_books_cache = {b:tuple(sorted(libs, key=sort_key)) for b, libs in c.items()} if not book_ids: book_ids = self._all_book_ids() # book_ids is usually 1 long. The loop will be faster than a comprehension r = {} default = () for b in book_ids: r[b] = self.vls_for_books_cache.get(b, default) return r

[belgeler] @read_api def user_categories_for_books(self, book_ids, proxy_metadata_map=None): ''' Return the user categories for the specified books. proxy_metadata_map is optional and is useful for a performance boost, in contexts where a ProxyMetadata object for the books already exists. It should be a mapping of book_ids to their corresponding ProxyMetadata objects. ''' user_cats = self._pref('user_categories', {}) pmm = proxy_metadata_map or {} ans = {}

for book_id in book_ids: proxy_metadata = pmm.get(book_id) or self._get_proxy_metadata(book_id) user_cat_vals = ans[book_id] = {} for ucat, categories in iteritems(user_cats): user_cat_vals[ucat] = res = [] for name, cat, ign in categories: try: field_obj = self.fields[cat] except KeyError: continue if field_obj.is_composite: v = field_obj.get_value_with_cache(book_id, lambda x:proxy_metadata) else: v = self._fast_field_for(field_obj, book_id)

if isinstance(v, (list, tuple)): if name in v: res.append([name, cat]) elif name == v: res.append([name, cat]) return ans

[belgeler] @write_api def embed_metadata(self, book_ids, only_fmts=None, report_error=None, report_progress=None): ''' Update metadata in all formats of the specified book_ids to current metadata in the database. ''' field = self.fields['formats'] from calibre.customize.ui import apply_null_metadata from calibre.ebooks.metadata.meta import set_metadata from calibre.ebooks.metadata.opf2 import pretty_print if only_fmts: only_fmts = {f.lower() for f in only_fmts}

def doit(fmt, mi, stream): with apply_null_metadata, pretty_print: set_metadata(stream, mi, stream_type=fmt, report_error=report_error) stream.seek(0, os.SEEK_END) return stream.tell()

for i, book_id in enumerate(book_ids): fmts = field.table.book_col_map.get(book_id, ()) if not fmts: continue mi = self._get_metadata(book_id) buf = BytesIO() if not self._copy_cover_to(book_id, buf): return cdata = buf.getvalue() if cdata: mi.cover_data = ('jpeg', cdata) try: path = self._field_for('path', book_id).replace('/', os.sep) except: continue for fmt in fmts: if only_fmts is not None and fmt.lower() not in only_fmts: continue try: name = self.fields['formats'].format_fname(book_id, fmt) except: continue if name and path: try: new_size = self.backend.apply_to_format(book_id, path, name, fmt, partial(doit, fmt, mi)) except Exception as e: if report_error is not None: tb = traceback.format_exc() if iswindows and isinstance(e, PermissionError) and e.filename and isinstance(e.filename, str): from calibre_extensions import winutil try: p = winutil.get_processes_using_files(e.filename) except OSError: pass else: path_map = {x['path']: x for x in p} tb = _('Could not open the file: "{}". It is already opened in the following programs:').format(e.filename) for path, x in path_map.items(): tb += '\n' + f'{x["app_name"]}: {path}' report_error(mi, fmt, tb) new_size = None else: raise if new_size is not None: self.format_metadata_cache[book_id].get(fmt, {})['size'] = new_size max_size = self.fields['formats'].table.update_fmt(book_id, fmt, name, new_size, self.backend) self.fields['size'].table.update_sizes({book_id: max_size}) if report_progress is not None: report_progress(i+1, len(book_ids), mi)@read_api def get_last_read_positions(self, book_id, fmt, user): fmt = fmt.upper() ans = [] for device, cfi, epoch, pos_frac in self.backend.execute( 'SELECT device,cfi,epoch,pos_frac FROM last_read_positions WHERE book=? AND format=? AND user=?', (book_id, fmt, user)): ans.append({'device':device, 'cfi': cfi, 'epoch':epoch, 'pos_frac':pos_frac}) return ans

@write_api def set_last_read_position(self, book_id, fmt, user='_', device='_', cfi=None, epoch=None, pos_frac=0): fmt = fmt.upper() device = device or '_' user = user or '_' if not cfi: self.backend.execute( 'DELETE FROM last_read_positions WHERE book=? AND format=? AND user=? AND device=?', (book_id, fmt, user, device)) else: self.backend.execute( 'INSERT OR REPLACE INTO last_read_positions(book,format,user,device,cfi,epoch,pos_frac) VALUES (?,?,?,?,?,?,?)', (book_id, fmt, user, device, cfi, epoch or time(), pos_frac))

@read_api def export_library(self, library_key, exporter, progress=None, abort=None): from polyglot.binary import as_hex_unicode key_prefix = as_hex_unicode(library_key) book_ids = self._all_book_ids() total = len(book_ids) + 2 has_fts = self.is_fts_enabled() if has_fts: total += 1 poff = 0 def report_progress(fname): nonlocal poff if progress is not None: progress(fname, poff, total) poff += 1 report_progress('metadata.db') pt = PersistentTemporaryFile('-export.db') pt.close() self.backend.backup_database(pt.name) dbkey = key_prefix + ':::' + 'metadata.db' with open(pt.name, 'rb') as f: exporter.add_file(f, dbkey) os.remove(pt.name) if has_fts: report_progress('full-text-search.db') pt = PersistentTemporaryFile('-export.db') pt.close() self.backend.backup_fts_database(pt.name) ftsdbkey = key_prefix + ':::full-text-search.db' with open(pt.name, 'rb') as f: exporter.add_file(f, ftsdbkey) os.remove(pt.name) notesdbkey = key_prefix + ':::notes.db' with PersistentTemporaryFile('-export.db') as pt: self.backend.export_notes_data(pt) pt.flush() pt.seek(0) report_progress('notes.db') exporter.add_file(pt, notesdbkey)

format_metadata = {} extra_files = {} metadata = {'format_data':format_metadata, 'metadata.db':dbkey, 'notes.db': notesdbkey, 'total':total, 'extra_files': extra_files} if has_fts: metadata['full-text-search.db'] = ftsdbkey for i, book_id in enumerate(book_ids): if abort is not None and abort.is_set(): return if progress is not None: report_progress(self._field_for('title', book_id)) format_metadata[book_id] = fm = {} for fmt in self._formats(book_id): mdata = self.format_metadata(book_id, fmt) key = f'{key_prefix}:{book_id}:{fmt}' fm[fmt] = key mtime = mdata.get('mtime') if mtime is not None: mtime = timestampfromdt(mtime) with exporter.start_file(key, mtime=mtime) as dest: self._copy_format_to(book_id, fmt, dest) cover_key = '{}:{}:{}'.format(key_prefix, book_id, '.cover') with exporter.start_file(cover_key) as dest: if not self.copy_cover_to(book_id, dest): dest.discard() else: fm['.cover'] = cover_key bp = self.field_for('path', book_id) extra_files[book_id] = ef = {} if bp: for (relpath, fobj, stat_result) in self.backend.iter_extra_files(book_id, bp, self.fields['formats']): key = f'{key_prefix}:{book_id}:.|{relpath}' with exporter.start_file(key, mtime=stat_result.st_mtime) as dest: shutil.copyfileobj(fobj, dest) ef[relpath] = key exporter.set_metadata(library_key, metadata) if progress is not None: progress(_('Completed'), total, total)

@read_api def annotations_map_for_book(self, book_id, fmt, user_type='local', user='viewer'): ans = {} for annot in self.backend.annotations_for_book(book_id, fmt, user_type, user): ans.setdefault(annot['type'], []).append(annot) return ans

@read_api def all_annotations_for_book(self, book_id): return tuple(self.backend.all_annotations_for_book(book_id))

@read_api def annotation_count_for_book(self, book_id): return self.backend.annotation_count_for_book(book_id)

@read_api def all_annotation_users(self): return tuple(self.backend.all_annotation_users())

@read_api def all_annotation_types(self): return tuple(self.backend.all_annotation_types())

@read_api def all_annotations(self, restrict_to_user=None, limit=None, annotation_type=None, ignore_removed=False, restrict_to_book_ids=None): return tuple(self.backend.all_annotations(restrict_to_user, limit, annotation_type, ignore_removed, restrict_to_book_ids))

@read_api def search_annotations( self, fts_engine_query, use_stemming=True, highlight_start=None, highlight_end=None, snippet_size=None, annotation_type=None, restrict_to_book_ids=None, restrict_to_user=None, ignore_removed=False ): return tuple(self.backend.search_annotations( fts_engine_query, use_stemming, highlight_start, highlight_end, snippet_size, annotation_type, restrict_to_book_ids, restrict_to_user, ignore_removed ))

@write_api def delete_annotations(self, annot_ids): self.backend.delete_annotations(annot_ids)

@write_api def update_annotations(self, annot_id_map): self.backend.update_annotations(annot_id_map)

@write_api def restore_annotations(self, book_id, annotations): from calibre.utils.date import EPOCH from calibre.utils.iso8601 import parse_iso8601 umap = defaultdict(list) for adata in annotations: key = adata['user_type'], adata['user'], adata['format'] a = adata['annotation'] ts = (parse_iso8601(a['timestamp']) - EPOCH).total_seconds() umap[key].append((a, ts)) for (user_type, user, fmt), annots_list in iteritems(umap): self._set_annotations_for_book(book_id, fmt, annots_list, user_type=user_type, user=user)

@write_api def set_annotations_for_book(self, book_id, fmt, annots_list, user_type='local', user='viewer'): self.backend.set_annotations_for_book(book_id, fmt, annots_list, user_type, user)

@write_api def merge_annotations_for_book(self, book_id, fmt, annots_list, user_type='local', user='viewer'): from calibre.utils.date import EPOCH from calibre.utils.iso8601 import parse_iso8601 amap = self._annotations_map_for_book(book_id, fmt, user_type=user_type, user=user) merge_annotations(annots_list, amap) alist = [] for val in itervalues(amap): for annot in val: ts = (parse_iso8601(annot['timestamp']) - EPOCH).total_seconds() alist.append((annot, ts)) self._set_annotations_for_book(book_id, fmt, alist, user_type=user_type, user=user)

@write_api def reindex_annotations(self): self.backend.reindex_annotations()

@read_api def are_paths_inside_book_dir(self, book_id, paths, sub_path=''): try: path = self._field_for('path', book_id).replace('/', os.sep) except: return set() return {x for x in paths if self.backend.is_path_inside_book_dir(x, path, sub_path)}

[belgeler] @write_api def add_extra_files(self, book_id, map_of_relpath_to_stream_or_path, replace=True, auto_rename=False): ' Add extra data files ' path = self._field_for('path', book_id).replace('/', os.sep) added = {} for relpath, stream_or_path in map_of_relpath_to_stream_or_path.items(): added[relpath] = bool(self.backend.add_extra_file(relpath, stream_or_path, path, replace, auto_rename)) self._clear_extra_files_cache(book_id) return added

[belgeler] @write_api def rename_extra_files(self, book_id, map_of_relpath_to_new_relpath, replace=False): ' Rename extra data files ' path = self._field_for('path', book_id).replace('/', os.sep) renamed = set() for relpath, newrelpath in map_of_relpath_to_new_relpath.items(): if self.backend.rename_extra_file(relpath, newrelpath, path, replace): renamed.add(relpath) self._clear_extra_files_cache(book_id) return renamed

[belgeler] @write_api def merge_extra_files(self, dest_id, src_ids, replace=False): ' Merge the extra files from src_ids into dest_id. Conflicting files are auto-renamed unless replace=True in which case they are replaced. ' added = set() path = self._field_for('path', dest_id) if path: path = path.replace('/', os.sep) for src_id in src_ids: book_path = self._field_for('path', src_id) if book_path: book_path = book_path.replace('/', os.sep) for (relpath, file_path, stat_result) in self.backend.iter_extra_files( src_id, book_path, self.fields['formats'], yield_paths=True): added.add(self.backend.add_extra_file(relpath, file_path, path, replace=replace, auto_rename=True)) self._clear_extra_files_cache(dest_id) return added

[belgeler] @read_api def list_extra_files(self, book_id, use_cache=False, pattern='') -> Tuple[ExtraFile, ...]: ''' Get information about extra files in the book's directory.

:param book_id: the database book id for the book :param pattern: the pattern of filenames to search for. Empty pattern matches all extra files. Patterns must use / as separator. Use the DATA_FILE_PATTERN constant to match files inside the data directory.

:return: A tuple of all extra files matching the specified pattern. Each element of the tuple is ExtraFile(relpath, file_path, stat_result). Where relpath is the relative path of the file to the book directory using / as a separator. stat_result is the result of calling os.stat() on the file. ''' ans = self.extra_files_cache.setdefault(book_id, {}).get(pattern) if ans is None or not use_cache: ans = [] path = self._field_for('path', book_id) if path: for (relpath, file_path, stat_result) in self.backend.iter_extra_files( book_id, path, self.fields['formats'], yield_paths=True, pattern=pattern ): ans.append(ExtraFile(relpath, file_path, stat_result)) self.extra_files_cache[book_id][pattern] = ans = tuple(ans) return ans

@read_api def copy_extra_file_to(self, book_id, relpath, stream_or_path): path = self._field_for('path', book_id).replace('/', os.sep) self.backend.copy_extra_file_to(book_id, path, relpath, stream_or_path)

@write_api def merge_book_metadata(self, dest_id, src_ids, replace_cover=False): dest_mi = self.get_metadata(dest_id) merged_identifiers = self._field_for('identifiers', dest_id) or {} orig_dest_comments = dest_mi.comments dest_cover = orig_dest_cover = self.cover(dest_id) had_orig_cover = bool(dest_cover) from calibre.utils.date import is_date_undefined def is_null_date(x): return x is None or is_date_undefined(x)

for src_id in src_ids: src_mi = self.get_metadata(src_id)

if src_mi.comments and orig_dest_comments != src_mi.comments: if not dest_mi.comments: dest_mi.comments = src_mi.comments else: dest_mi.comments = str(dest_mi.comments) + '\n\n' + str(src_mi.comments) if src_mi.title and dest_mi.is_null('title'): dest_mi.title = src_mi.title dest_mi.title_sort = src_mi.title_sort if (src_mi.authors and src_mi.authors[0] != _('Unknown')) and (not dest_mi.authors or dest_mi.authors[0] == _('Unknown')): dest_mi.authors = src_mi.authors dest_mi.author_sort = src_mi.author_sort if src_mi.tags: if not dest_mi.tags: dest_mi.tags = src_mi.tags else: dest_mi.tags.extend(src_mi.tags) if not dest_cover or replace_cover: src_cover = self.cover(src_id) if src_cover: dest_cover = src_cover replace_cover = False if not dest_mi.publisher: dest_mi.publisher = src_mi.publisher if not dest_mi.rating: dest_mi.rating = src_mi.rating if not dest_mi.series: dest_mi.series = src_mi.series dest_mi.series_index = src_mi.series_index if is_null_date(dest_mi.pubdate) and not is_null_date(src_mi.pubdate): dest_mi.pubdate = src_mi.pubdate src_identifiers = self.field_for('identifier', src_id) or {} src_identifiers.update(merged_identifiers) merged_identifiers = src_identifiers.copy()

if merged_identifiers: dest_mi.set_identifiers(merged_identifiers) self._set_metadata(dest_id, dest_mi, ignore_errors=False)

if dest_cover and (not had_orig_cover or dest_cover is not orig_dest_cover): self._set_cover({dest_id: dest_cover})

for key in self.field_metadata: # loop thru all defined fields fm = self.field_metadata[key] if not fm['is_custom']: continue dt = fm['datatype'] label = fm['label'] try: field = self.field_metadata.label_to_key(label) except ValueError: continue # Get orig_dest_comments before it gets changed if dt == 'comments': orig_dest_value = self._field_for(field, dest_id)

for src_id in src_ids: dest_value = self._field_for(field, dest_id) src_value = self._field_for(field, src_id) if (dt == 'comments' and src_value and src_value != orig_dest_value): if not dest_value: self._set_field(field, {dest_id: src_value}) else: dest_value = str(dest_value) + '\n\n' + str(src_value) self._set_field(field, {dest_id: dest_value}) if (dt in {'bool', 'int', 'float', 'rating', 'datetime'} and dest_value is None): self._set_field(field, {dest_id: src_value}) if (dt == 'series' and not dest_value and src_value): src_index = self._field_for(field + '_index', src_id) self._set_field(field, {dest_id:src_value}) self._set_field(field + '_index', {dest_id:src_index}) if ((dt == 'enumeration' or (dt == 'text' and not fm['is_multiple'])) and not dest_value): self._set_field(field, {dest_id:src_value}) if (dt == 'text' and fm['is_multiple'] and src_value): if not dest_value: dest_value = src_value else: dest_value = list(dest_value) dest_value.extend(src_value) self._set_field(field, {dest_id: dest_value})def import_library(library_key, importer, library_path, progress=None, abort=None): from calibre.db.backend import DB metadata = importer.metadata[library_key] total = metadata['total'] poff = 0 def report_progress(fname): nonlocal poff if progress is not None: progress(fname, poff, total) poff += 1 report_progress('metadata.db') if abort is not None and abort.is_set(): return importer.save_file(metadata['metadata.db'], 'metadata.db for ' + library_path, os.path.join(library_path, 'metadata.db')) if 'full-text-search.db' in metadata: if progress is not None: progress('full-text-search.db', 1, total) if abort is not None and abort.is_set(): return poff += 1 importer.save_file(metadata['full-text-search.db'], 'full-text-search.db for ' + library_path, os.path.join(library_path, 'full-text-search.db')) if abort is not None and abort.is_set(): return if 'notes.db' in metadata: import zipfile notes_dir = os.path.join(library_path, NOTES_DIR_NAME) os.makedirs(notes_dir, exist_ok=True) with importer.start_file(metadata['notes.db'], 'notes.db for ' + library_path) as stream: stream.check_hash = False with zipfile.ZipFile(stream) as zf: for zi in zf.infolist(): tpath = zf._extract_member(zi, notes_dir, None) date_time = mktime(zi.date_time + (0, 0, -1)) os.utime(tpath, (date_time, date_time)) if abort is not None and abort.is_set(): return if importer.corrupted_files: raise ValueError('Corrupted files:\n' + '\n'.join(importer.corrupted_files)) cache = Cache(DB(library_path, load_user_formatter_functions=False)) cache.init()

format_data = {int(book_id):data for book_id, data in iteritems(metadata['format_data'])} extra_files = {int(book_id):data for book_id, data in metadata.get('extra_files', {}).items()} for i, (book_id, fmt_key_map) in enumerate(iteritems(format_data)): if abort is not None and abort.is_set(): return title = cache._field_for('title', book_id) if progress is not None: progress(title, i + poff, total) cache._update_path((book_id,), mark_as_dirtied=False) for fmt, fmtkey in fmt_key_map.items(): if fmt == '.cover': with importer.start_file(fmtkey, _('Cover for %s') % title) as stream: path = cache._field_for('path', book_id).replace('/', os.sep) cache.backend.set_cover(book_id, path, stream, no_processing=True) else: with importer.start_file(fmtkey, _('{0} format for {1}').format(fmt.upper(), title)) as stream: size, fname = cache._do_add_format(book_id, fmt, stream, mtime=stream.mtime) cache.fields['formats'].table.update_fmt(book_id, fmt, fname, size, cache.backend) for relpath, efkey in extra_files.get(book_id, {}).items(): with importer.start_file(efkey, _('Extra file {0} for book {1}').format(relpath, title)) as stream: path = cache._field_for('path', book_id).replace('/', os.sep) cache.backend.add_extra_file(relpath, stream, path) cache.dump_metadata({book_id}) if importer.corrupted_files: raise ValueError('Corrupted files:\n' + '\n'.join(importer.corrupted_files)) if progress is not None: progress(_('Completed'), total, total) return cache # }}}

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.devices.interface

calibre.devices.interface öğesinin kaynak kodu

__license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' import os from collections import namedtuple from calibre import prints from calibre.constants import iswindows from calibre.customize import Plugin class OpenPopupMessage:

def __init__(self, title='', message='', level='info', skip_dialog_skip_precheck=True): self.title = title self.message = message self.level = level self.skip_dialog_skip_precheck = skip_dialog_skip_precheck

[belgeler] class DevicePlugin(Plugin): """ Defines the interface that should be implemented by backends that communicate with an e-book reader. """ type = _('Device interface')

#: Ordered list of supported formats FORMATS = ["lrf", "rtf", "pdf", "txt"] # If True, the config dialog will not show the formats box HIDE_FORMATS_CONFIG_BOX = False

#: VENDOR_ID can be either an integer, a list of integers or a dictionary #: If it is a dictionary, it must be a dictionary of dictionaries, #: of the form:: #: #: { #: integer_vendor_id : { product_id : [list of BCDs], ... }, #: ... #: } #: VENDOR_ID = 0x0000

#: An integer or a list of integers PRODUCT_ID = 0x0000 #: BCD can be either None to not distinguish between devices based on BCD, or #: it can be a list of the BCD numbers of all devices supported by this driver. BCD = None

#: Height for thumbnails on the device THUMBNAIL_HEIGHT = 68

#: Compression quality for thumbnails. Set this closer to 100 to have better #: quality thumbnails with fewer compression artifacts. Of course, the #: thumbnails get larger as well. THUMBNAIL_COMPRESSION_QUALITY = 75

#: Set this to True if the device supports updating cover thumbnails during #: sync_booklists. Setting it to true will ask device.py to refresh the #: cover thumbnails during book matching WANTS_UPDATED_THUMBNAILS = False

#: Whether the metadata on books can be set via the GUI. CAN_SET_METADATA = ['title', 'authors', 'collections']

#: Whether the device can handle device_db metadata plugboards CAN_DO_DEVICE_DB_PLUGBOARD = False

# Set this to None if the books on the device are files that the GUI can # access in order to add the books from the device to the library BACKLOADING_ERROR_MESSAGE = _('Cannot get files from this device')

#: Path separator for paths to books on device path_sep = os.sep

#: Icon for this device icon = 'reader.png'

# Encapsulates an annotation fetched from the device UserAnnotation = namedtuple('Annotation','type, value')

#: GUI displays this as a message if not None in the status bar. Useful if opening can take a #: long time OPEN_FEEDBACK_MESSAGE = None

#: Set of extensions that are "virtual books" on the device #: and therefore cannot be viewed/saved/added to library. #: For example: ``frozenset(['kobo'])`` VIRTUAL_BOOK_EXTENSIONS = frozenset()

#: Message to display to user for virtual book extensions. VIRTUAL_BOOK_EXTENSION_MESSAGE = None

#: Whether to nuke comments in the copy of the book sent to the device. If #: not None this should be short string that the comments will be replaced #: by. NUKE_COMMENTS = None

#: If True indicates that this driver completely manages device detection, #: ejecting and so forth. If you set this to True, you *must* implement the #: detect_managed_devices and debug_managed_device_detection methods. #: A driver with this set to true is responsible for detection of devices, #: managing a blacklist of devices, a list of ejected devices and so forth. #: calibre will periodically call the detect_managed_devices() method and #: if it returns a detected device, calibre will call open(). open() will #: be called every time a device is returned even if previous calls to open() #: failed, therefore the driver must maintain its own blacklist of failed #: devices. Similarly, when ejecting, calibre will call eject() and then #: assuming the next call to detect_managed_devices() returns None, it will #: call post_yank_cleanup(). MANAGES_DEVICE_PRESENCE = False

#: If set the True, calibre will call the :meth:`get_driveinfo()` method #: after the books lists have been loaded to get the driveinfo. SLOW_DRIVEINFO = False

#: If set to True, calibre will ask the user if they want to manage the #: device with calibre, the first time it is detected. If you set this to #: True you must implement :meth:`get_device_uid()` and #: :meth:`ignore_connected_device()` and #: :meth:`get_user_blacklisted_devices` and #: :meth:`set_user_blacklisted_devices` ASK_TO_ALLOW_CONNECT = False

#: Set this to a dictionary of the form {'title':title, 'msg':msg, 'det_msg':detailed_msg} to have calibre popup #: a message to the user after some callbacks are run (currently only upload_books). #: Be careful to not spam the user with too many messages. This variable is checked after *every* callback, #: so only set it when you really need to. user_feedback_after_callback = None

@classmethod def get_gui_name(cls): if hasattr(cls, 'gui_name'): return cls.gui_name if hasattr(cls, '__name__'): return cls.__name__ return cls.name

[belgeler] @classmethod def get_open_popup_message(self): ' GUI displays this as a non-modal popup. Should be an instance of OpenPopupMessage ' return None

# Device detection {{{ def test_bcd(self, bcdDevice, bcd): if bcd is None or len(bcd) == 0: return True for c in bcd: if c == bcdDevice: return True return False

[belgeler] def is_usb_connected(self, devices_on_system, debug=False, only_presence=False): ''' Return True, device_info if a device handled by this plugin is currently connected.

:param devices_on_system: List of devices currently connected

''' vendors_on_system = {x[0] for x in devices_on_system} vendors = set(self.VENDOR_ID) if hasattr(self.VENDOR_ID, '__len__') else {self.VENDOR_ID} if hasattr(self.VENDOR_ID, 'keys'): products = [] for ven in self.VENDOR_ID: products.extend(self.VENDOR_ID[ven].keys()) else: products = self.PRODUCT_ID if hasattr(self.PRODUCT_ID, '__len__') else [self.PRODUCT_ID]

ch = self.can_handle_windows if iswindows else self.can_handle for vid in vendors_on_system.intersection(vendors): for dev in devices_on_system: cvid, pid, bcd = dev[:3] if cvid == vid: if pid in products: if hasattr(self.VENDOR_ID, 'keys'): try: cbcd = self.VENDOR_ID[vid][pid] except KeyError: # Vendor vid does not have product pid, pid # exists for some other vendor in this # device continue else: cbcd = self.BCD if self.test_bcd(bcd, cbcd): if debug: prints(dev) if ch(dev, debug=debug): return True, dev return False, None

[belgeler] def detect_managed_devices(self, devices_on_system, force_refresh=False): ''' Called only if MANAGES_DEVICE_PRESENCE is True.

Scan for devices that this driver can handle. Should return a device object if a device is found. This object will be passed to the open() method as the connected_device. If no device is found, return None. The returned object can be anything, calibre does not use it, it is only passed to open().

This method is called periodically by the GUI, so make sure it is not too resource intensive. Use a cache to avoid repeatedly scanning the system.

:param devices_on_system: Set of USB devices found on the system.

:param force_refresh: If True and the driver uses a cache to prevent repeated scanning, the cache must be flushed.

''' raise NotImplementedError()

[belgeler] def debug_managed_device_detection(self, devices_on_system, output): ''' Called only if MANAGES_DEVICE_PRESENCE is True.

Should write information about the devices detected on the system to output, which is a file like object.

Should return True if a device was detected and successfully opened, otherwise False. ''' raise NotImplementedError()

# }}}

[belgeler] def reset(self, key='-1', log_packets=False, report_progress=None, detected_device=None): """ :param key: The key to unlock the device :param log_packets: If true the packet stream to/from the device is logged :param report_progress: Function that is called with a % progress (number between 0 and 100) for various tasks. If it is called with -1 that means that the task does not have any progress information :param detected_device: Device information from the device scanner

""" raise NotImplementedError()

[belgeler] def can_handle_windows(self, usbdevice, debug=False): ''' Optional method to perform further checks on a device to see if this driver is capable of handling it. If it is not it should return False. This method is only called after the vendor, product ids and the bcd have matched, so it can do some relatively time intensive checks. The default implementation returns True. This method is called only on Windows. See also :meth:`can_handle`.

Note that for devices based on USBMS this method by default delegates to :meth:`can_handle`. So you only need to override :meth:`can_handle` in your subclass of USBMS.

:param usbdevice: A usbdevice as returned by :func:`calibre.devices.winusb.scan_usb_devices` ''' return True

[belgeler] def can_handle(self, device_info, debug=False): ''' Unix version of :meth:`can_handle_windows`.

:param device_info: Is a tuple of (vid, pid, bcd, manufacturer, product, serial number)

'''

return True
can_handle.is_base_class_implementation = True

[belgeler] def open(self, connected_device, library_uuid): ''' Perform any device specific initialization. Called after the device is detected but before any other functions that communicate with the device. For example: For devices that present themselves as USB Mass storage devices, this method would be responsible for mounting the device or if the device has been automounted, for finding out where it has been mounted. The method :meth:`calibre.devices.usbms.device.Device.open` has an implementation of this function that should serve as a good example for USB Mass storage devices.

This method can raise an OpenFeedback exception to display a message to the user.

:param connected_device: The device that we are trying to open. It is a tuple of (vendor id, product id, bcd, manufacturer name, product name, device serial number). However, some devices have no serial number and on Windows only the first three fields are present, the rest are None.

:param library_uuid: The UUID of the current calibre library. Can be None if there is no library (for example when used from the command line).

''' raise NotImplementedError()

[belgeler] def eject(self): ''' Un-mount / eject the device from the OS. This does not check if there are pending GUI jobs that need to communicate with the device.

NOTE: That this method may not be called on the same thread as the rest of the device methods. ''' raise NotImplementedError()

[belgeler] def post_yank_cleanup(self): ''' Called if the user yanks the device without ejecting it first. ''' raise NotImplementedError()

[belgeler] def set_progress_reporter(self, report_progress): ''' Set a function to report progress information.

:param report_progress: Function that is called with a % progress (number between 0 and 100) for various tasks. If it is called with -1 that means that the task does not have any progress information

''' raise NotImplementedError()

[belgeler] def get_device_information(self, end_session=True): """ Ask device for device information. See L{DeviceInfoQuery}.

:return: (device name, device version, software version on device, MIME type) The tuple can optionally have a fifth element, which is a drive information dictionary. See usbms.driver for an example.

""" raise NotImplementedError()

[belgeler] def get_driveinfo(self): ''' Return the driveinfo dictionary. Usually called from get_device_information(), but if loading the driveinfo is slow for this driver, then it should set SLOW_DRIVEINFO. In this case, this method will be called by calibre after the book lists have been loaded. Note that it is not called on the device thread, so the driver should cache the drive info in the books() method and this function should return the cached data. ''' return {}

[belgeler] def card_prefix(self, end_session=True): ''' Return a 2 element list of the prefix to paths on the cards. If no card is present None is set for the card's prefix. E.G. ('/place', '/place2') (None, 'place2') ('place', None) (None, None) ''' raise NotImplementedError()

[belgeler] def total_space(self, end_session=True): """ Get total space available on the mountpoints: 1. Main memory 2. Memory Card A 3. Memory Card B

:return: A 3 element list with total space in bytes of (1, 2, 3). If a particular device doesn't have any of these locations it should return 0.

""" raise NotImplementedError()

[belgeler] def free_space(self, end_session=True): """ Get free space available on the mountpoints: 1. Main memory 2. Card A 3. Card B

:return: A 3 element list with free space in bytes of (1, 2, 3). If a particular device doesn't have any of these locations it should return -1.

""" raise NotImplementedError()

[belgeler] def books(self, oncard=None, end_session=True): """ Return a list of e-books on the device.

:param oncard: If 'carda' or 'cardb' return a list of e-books on the specific storage card, otherwise return list of e-books in main memory of device. If a card is specified and no books are on the card return empty list.

:return: A BookList.

""" raise NotImplementedError()

[belgeler] def upload_books(self, files, names, on_card=None, end_session=True, metadata=None): ''' Upload a list of books to the device. If a file already exists on the device, it should be replaced. This method should raise a :class:`FreeSpaceError` if there is not enough free space on the device. The text of the FreeSpaceError must contain the word "card" if ``on_card`` is not None otherwise it must contain the word "memory".

:param files: A list of paths :param names: A list of file names that the books should have once uploaded to the device. len(names) == len(files) :param metadata: If not None, it is a list of :class:`Metadata` objects. The idea is to use the metadata to determine where on the device to put the book. len(metadata) == len(files). Apart from the regular cover (path to cover), there may also be a thumbnail attribute, which should be used in preference. The thumbnail attribute is of the form (width, height, cover_data as jpeg).

:return: A list of 3-element tuples. The list is meant to be passed to :meth:`add_books_to_metadata`. ''' raise NotImplementedError()

[belgeler] @classmethod def add_books_to_metadata(cls, locations, metadata, booklists): ''' Add locations to the booklists. This function must not communicate with the device.

:param locations: Result of a call to L{upload_books} :param metadata: List of :class:`Metadata` objects, same as for :meth:`upload_books`. :param booklists: A tuple containing the result of calls to (:meth:`books(oncard=None)`, :meth:`books(oncard='carda')`, :meth`books(oncard='cardb')`).

''' raise NotImplementedError()

[belgeler] def delete_books(self, paths, end_session=True): ''' Delete books at paths on device. ''' raise NotImplementedError()

[belgeler] @classmethod def remove_books_from_metadata(cls, paths, booklists): ''' Remove books from the metadata list. This function must not communicate with the device.

:param paths: paths to books on the device. :param booklists: A tuple containing the result of calls to (:meth:`books(oncard=None)`, :meth:`books(oncard='carda')`, :meth`books(oncard='cardb')`).

''' raise NotImplementedError()

[belgeler] def sync_booklists(self, booklists, end_session=True): ''' Update metadata on device.

:param booklists: A tuple containing the result of calls to (:meth:`books(oncard=None)`, :meth:`books(oncard='carda')`, :meth`books(oncard='cardb')`).

''' raise NotImplementedError()

[belgeler] def get_file(self, path, outfile, end_session=True): ''' Read the file at ``path`` on the device and write it to outfile.

:param outfile: file object like ``sys.stdout`` or the result of an :func:`open` call.

''' raise NotImplementedError()

[belgeler] @classmethod def config_widget(cls): ''' Should return a QWidget. The QWidget contains the settings for the device interface ''' raise NotImplementedError()

[belgeler] @classmethod def save_settings(cls, settings_widget): ''' Should save settings to disk. Takes the widget created in :meth:`config_widget` and saves all settings to disk. ''' raise NotImplementedError()

[belgeler] @classmethod def settings(cls): ''' Should return an opts object. The opts object should have at least one attribute `format_map` which is an ordered list of formats for the device. ''' raise NotImplementedError()

[belgeler] def set_plugboards(self, plugboards, pb_func): ''' provide the driver the current set of plugboards and a function to select a specific plugboard. This method is called immediately before add_books and sync_booklists.

pb_func is a callable with the following signature:: def pb_func(device_name, format, plugboards)

You give it the current device name (either the class name or DEVICE_PLUGBOARD_NAME), the format you are interested in (a 'real' format or 'device_db'), and the plugboards (you were given those by set_plugboards, the same place you got this method).

:return: None or a single plugboard instance.

''' pass

[belgeler] def set_driveinfo_name(self, location_code, name): ''' Set the device name in the driveinfo file to 'name'. This setting will persist until the file is re-created or the name is changed again.

Non-disk devices should implement this method based on the location codes returned by the get_device_information() method. ''' pass

[belgeler] def prepare_addable_books(self, paths): ''' Given a list of paths, returns another list of paths. These paths point to addable versions of the books.

If there is an error preparing a book, then instead of a path, the position in the returned list for that book should be a three tuple: (original_path, the exception instance, traceback) ''' return paths

[belgeler] def startup(self): ''' Called when calibre is starting the device. Do any initialization required. Note that multiple instances of the class can be instantiated, and thus __init__ can be called multiple times, but only one instance will have this method called. This method is called on the device thread, not the GUI thread. ''' pass

[belgeler] def shutdown(self): ''' Called when calibre is shutting down, either for good or in preparation to restart. Do any cleanup required. This method is called on the device thread, not the GUI thread. ''' pass

[belgeler] def get_device_uid(self): ''' Must return a unique id for the currently connected device (this is called immediately after a successful call to open()). You must implement this method if you set ASK_TO_ALLOW_CONNECT = True ''' raise NotImplementedError()

[belgeler] def ignore_connected_device(self, uid): ''' Should ignore the device identified by uid (the result of a call to get_device_uid()) in the future. You must implement this method if you set ASK_TO_ALLOW_CONNECT = True. Note that this function is called immediately after open(), so if open() caches some state, the driver should reset that state. ''' raise NotImplementedError()

[belgeler] def get_user_blacklisted_devices(self): ''' Return map of device uid to friendly name for all devices that the user has asked to be ignored. ''' return {}

[belgeler] def set_user_blacklisted_devices(self, devices): ''' Set the list of device uids that should be ignored by this driver. ''' pass

[belgeler] def specialize_global_preferences(self, device_prefs): ''' Implement this method if your device wants to override a particular preference. You must ensure that all call sites that want a preference that can be overridden use device_prefs['something'] instead of prefs['something']. Your method should call device_prefs.set_overrides(pref=val, pref=val, ...). Currently used for: metadata management (prefs['manage_device_metadata']) ''' device_prefs.set_overrides()

[belgeler] def set_library_info(self, library_name, library_uuid, field_metadata): ''' Implement this method if you want information about the current calibre library. This method is called at startup and when the calibre library changes while connected. ''' pass

# Dynamic control interface. # The following methods are probably called on the GUI thread. Any driver # that implements these methods must take pains to be thread safe, because # the device_manager might be using the driver at the same time that one of # these methods is called.

[belgeler] def is_dynamically_controllable(self): ''' Called by the device manager when starting plugins. If this method returns a string, then a) it supports the device manager's dynamic control interface, and b) that name is to be used when talking to the plugin.

This method can be called on the GUI thread. A driver that implements this method must be thread safe. ''' return None

[belgeler] def start_plugin(self): ''' This method is called to start the plugin. The plugin should begin to accept device connections however it does that. If the plugin is already accepting connections, then do nothing.

This method can be called on the GUI thread. A driver that implements this method must be thread safe. ''' pass

[belgeler] def stop_plugin(self): ''' This method is called to stop the plugin. The plugin should no longer accept connections, and should cleanup behind itself. It is likely that this method should call shutdown. If the plugin is already not accepting connections, then do nothing.

This method can be called on the GUI thread. A driver that implements this method must be thread safe. ''' pass

[belgeler] def get_option(self, opt_string, default=None): ''' Return the value of the option indicated by opt_string. This method can be called when the plugin is not started. Return None if the option does not exist.

This method can be called on the GUI thread. A driver that implements this method must be thread safe. ''' return default

[belgeler] def set_option(self, opt_string, opt_value): ''' Set the value of the option indicated by opt_string. This method can be called when the plugin is not started.

This method can be called on the GUI thread. A driver that implements this method must be thread safe. ''' pass

[belgeler] def is_running(self): ''' Return True if the plugin is started, otherwise false

This method can be called on the GUI thread. A driver that implements this method must be thread safe. ''' return False

[belgeler] def synchronize_with_db(self, db, book_id, book_metadata, first_call): ''' Called during book matching when a book on the device is matched with a book in calibre's db. The method is responsible for synchronizing data from the device to calibre's db (if needed).

The method must return a two-value tuple. The first value is a set of calibre book ids changed if calibre's database was changed or None if the database was not changed. If the first value is an empty set then the metadata for the book on the device is updated with calibre's metadata and given back to the device, but no GUI refresh of that book is done. This is useful when the calibre data is correct but must be sent to the device.

The second value is itself a 2-value tuple. The first value in the tuple specifies whether a book format should be sent to the device. The intent is to permit verifying that the book on the device is the same as the book in calibre. This value must be None if no book is to be sent, otherwise return the base file name on the device (a string like foobar.epub). Be sure to include the extension in the name. The device subsystem will construct a send_books job for all books with not- None returned values. Note: other than to later retrieve the extension, the name is ignored in cases where the device uses a template to generate the file name, which most do. The second value in the returned tuple indicated whether the format is future-dated. Return True if it is, otherwise return False. calibre will display a dialog to the user listing all future dated books.

Extremely important: this method is called on the GUI thread. It must be threadsafe with respect to the device manager's thread.

book_id: the calibre id for the book in the database. book_metadata: the Metadata object for the book coming from the device. first_call: True if this is the first call during a sync, False otherwise ''' return (None, (None, False))

[belgeler] class BookList(list): ''' A list of books. Each Book object must have the fields

#. title #. authors #. size (file size of the book) #. datetime (a UTC time tuple) #. path (path on the device to the book) #. thumbnail (can be None) thumbnail is either a str/bytes object with the image data or it should have an attribute image_path that stores an absolute (platform native) path to the image #. tags (a list of strings, can be empty).

'''

__getslice__ = None __setslice__ = None def __init__(self, oncard, prefix, settings): pass

[belgeler] def supports_collections(self): ''' Return True if the device supports collections for this book list. ''' raise NotImplementedError()

[belgeler] def add_book(self, book, replace_metadata): ''' Add the book to the booklist. Intent is to maintain any device-internal metadata. Return True if booklists must be sync'ed ''' raise NotImplementedError()

[belgeler] def remove_book(self, book): ''' Remove a book from the booklist. Correct any device metadata at the same time ''' raise NotImplementedError()

[belgeler] def get_collections(self, collection_attributes): ''' Return a dictionary of collections created from collection_attributes. Each entry in the dictionary is of the form collection name:[list of books]

The list of books is sorted by book title, except for collections created from series, in which case series_index is used.

:param collection_attributes: A list of attributes of the Book object

''' raise NotImplementedError()

class CurrentlyConnectedDevice:

def __init__(self): self._device = None

@property def device(self): return self._device

# A device driver can check if a device is currently connected to calibre using # the following code:: # from calibre.device.interface import currently_connected_device # if currently_connected_device.device is None: # # no device connected # The device attribute will be either None or the device driver object # (DevicePlugin instance) for the currently connected device. currently_connected_device = CurrentlyConnectedDevice()

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.devices.usbms.cli

calibre.devices.usbms.cli öğesinin kaynak kodu

__license__ = 'GPL v3' __copyright__ = '2009, John Schember <john@nachtimwald.com>' __docformat__ = 'restructuredtext en'

import os import shutil import time from calibre import fsync from calibre.devices.errors import PathError from calibre.utils.filenames import case_preserving_open_file class File:

def __init__(self, path): stats = os.stat(path) self.is_dir = os.path.isdir(path) self.is_readonly = not os.access(path, os.W_OK) self.ctime = stats.st_ctime self.wtime = stats.st_mtime self.size = stats.st_size if path.endswith(os.sep): path = path[:-1] self.path = path self.name = os.path.basename(path)

def check_transfer(infile, dest): infile.seek(0) dest.seek(0) return infile.read() == dest.read()

[belgeler] class CLI:

def get_file(self, path, outfile, end_session=True): path = self.munge_path(path) with open(path, 'rb') as src: shutil.copyfileobj(src, outfile)

def put_file(self, infile, path, replace_file=False, end_session=True): path = self.munge_path(path) close = False if not hasattr(infile, 'read'): infile, close = open(infile, 'rb'), True infile.seek(0) if os.path.isdir(path): path = os.path.join(path, infile.name) if not replace_file and os.path.exists(path): raise PathError('File already exists: ' + path) dest, actual_path = case_preserving_open_file(path) with dest: try: shutil.copyfileobj(infile, dest) except OSError: print('WARNING: First attempt to send file to device failed') time.sleep(0.2) infile.seek(0) dest.seek(0) dest.truncate() shutil.copyfileobj(infile, dest) fsync(dest) # if not check_transfer(infile, dest): raise Exception('Transfer failed') if close: infile.close() return actual_path def munge_path(self, path): if path.startswith('/') and not (path.startswith(self._main_prefix) or (self._card_a_prefix and path.startswith(self._card_a_prefix)) or (self._card_b_prefix and path.startswith(self._card_b_prefix))): path = self._main_prefix + path[1:] elif path.startswith('carda:'): path = path.replace('carda:', self._card_a_prefix[:-1]) elif path.startswith('cardb:'): path = path.replace('cardb:', self._card_b_prefix[:-1]) return path def list(self, path, recurse=False, end_session=True, munge=True): if munge: path = self.munge_path(path) if os.path.isfile(path): return [(os.path.dirname(path), [File(path)])] entries = [File(os.path.join(path, f)) for f in os.listdir(path)] dirs = [(path, entries)] for _file in entries: if recurse and _file.is_dir: dirs[len(dirs):] = self.list(_file.path, recurse=True, munge=False) return dirs def mkdir(self, path, end_session=True): if self.SUPPORTS_SUB_DIRS: path = self.munge_path(path) os.mkdir(path)

def rm(self, path, end_session=True): path = self.munge_path(path) self.delete_books([path])

def touch(self, path, end_session=True): path = self.munge_path(path) if not os.path.exists(path): open(path, 'wb').close() if not os.path.isdir(path): os.utime(path, None)

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.devices.usbms.device

calibre.devices.usbms.device öğesinin kaynak kodu

__license__ = 'GPL v3' __copyright__ = '2009, John Schember <john at nachtimwald.com> ' \ '2009, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en'

''' Generic device driver. This is not a complete stand alone driver. It is intended to be subclassed with the relevant parts implemented for a particular device. This class handles device detection. '''

import glob import os import re import subprocess import sys import time from collections import namedtuple from itertools import repeat from calibre import prints from calibre.constants import is_debugging, isfreebsd, islinux, ismacos, iswindows from calibre.devices.errors import DeviceError from calibre.devices.interface import DevicePlugin from calibre.devices.usbms.deviceconfig import DeviceConfig from calibre.utils.filenames import ascii_filename as sanitize from polyglot.builtins import iteritems, string_or_bytes if ismacos: osx_sanitize_name_pat = re.compile(r'[.-]')

if iswindows: usb_info_cache = {}

def eject_exe(): base = sys.extensions_location if hasattr(sys, 'new_app_layout') else os.path.dirname(sys.executable) return os.path.join(base, 'calibre-eject.exe')

class USBDevice:

def __init__(self, dev): self.idVendor = dev[0] self.idProduct = dev[1] self.bcdDevice = dev[2] if iswindows: # Getting this information requires communicating with the device # we only do that in the can_handle_windows() method, if needed. self.manufacturer = self.serial = self.product = None else: self.manufacturer = dev[3] self.product = dev[4] self.serial = dev[5]

def match_serial(self, serial): return self.serial and self.serial == serial def match_numbers(self, vid, pid, bcd): return self.idVendor == vid and self.idProduct == pid and self.bcdDevice == bcd def match_strings(self, vid, pid, bcd, man, prod): if not self.match_numbers(vid, pid, bcd): return False if man == self.manufacturer and prod == self.product: return True # As of macOS 10.11.4 Apple started mangling the names returned via the # IOKit registry. See # https://www.mobileread.com/forums/showthread.php?t=273213 m = osx_sanitize_name_pat.sub('_', (self.manufacturer or '')) p = osx_sanitize_name_pat.sub('_', (self.product or '')) return m == man and p == prod

[belgeler] class Device(DeviceConfig, DevicePlugin):

''' This class provides logic common to all drivers for devices that export themselves as USB Mass Storage devices. Provides implementations for mounting/ejecting of USBMS devices on all platforms. '''

VENDOR_ID = 0x0 PRODUCT_ID = 0x0 BCD = None

VENDOR_NAME = None

#: String identifying the main memory of the device in the Windows PnP id #: strings #: This can be None, string, list of strings or compiled regex WINDOWS_MAIN_MEM = None

#: String identifying the first card of the device in the Windows PnP id #: strings #: This can be None, string, list of strings or compiled regex WINDOWS_CARD_A_MEM = None

#: String identifying the second card of the device in the Windows PnP id #: strings #: This can be None, string, list of strings or compiled regex WINDOWS_CARD_B_MEM = None

#: Used by the new driver detection to disambiguate main memory from #: storage cards. Should be a regular expression that matches the #: main memory mount point assigned by macOS OSX_MAIN_MEM_VOL_PAT = None OSX_EJECT_COMMAND = ['diskutil', 'eject']

MAIN_MEMORY_VOLUME_LABEL = '' STORAGE_CARD_VOLUME_LABEL = '' STORAGE_CARD2_VOLUME_LABEL = None

EBOOK_DIR_MAIN = '' EBOOK_DIR_CARD_A = '' EBOOK_DIR_CARD_B = '' DELETE_EXTS = []

# USB disk-based devices can see the book files on the device, so can # copy these back to the library BACKLOADING_ERROR_MESSAGE = None

#: The maximum length of paths created on the device MAX_PATH_LEN = 250

#: Put news in its own folder NEWS_IN_FOLDER = True

[belgeler] def reset(self, key='-1', log_packets=False, report_progress=None, detected_device=None): self._main_prefix = self._card_a_prefix = self._card_b_prefix = None self.detected_device = None if detected_device is None else USBDevice(detected_device) self.set_progress_reporter(report_progress)

[belgeler] def set_progress_reporter(self, report_progress): self.report_progress = report_progress self.report_progress = report_progress if self.report_progress is None: self.report_progress = lambda x, y: x

[belgeler] def card_prefix(self, end_session=True): return (self._card_a_prefix, self._card_b_prefix)

@classmethod def _windows_space(cls, prefix): if not prefix: return 0, 0 prefix = prefix[:-1] from calibre_extensions import winutil try: available_space, total_space, free_space = winutil.get_disk_free_space(prefix) except OSError as err: if err.winerror == winutil.ERROR_NOT_READY: # Disk not ready time.sleep(3) available_space, total_space, free_space = winutil.get_disk_free_space(prefix) else: raise return total_space, available_space

[belgeler] def total_space(self, end_session=True): msz = casz = cbsz = 0 if not iswindows: if self._main_prefix is not None: stats = os.statvfs(self._main_prefix) msz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree) if self._card_a_prefix is not None: stats = os.statvfs(self._card_a_prefix) casz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree) if self._card_b_prefix is not None: stats = os.statvfs(self._card_b_prefix) cbsz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree) else: msz = self._windows_space(self._main_prefix)[0] casz = self._windows_space(self._card_a_prefix)[0] cbsz = self._windows_space(self._card_b_prefix)[0]

return (msz, casz, cbsz)

[belgeler] def free_space(self, end_session=True): msz = casz = cbsz = 0 if not iswindows: if self._main_prefix is not None: stats = os.statvfs(self._main_prefix) msz = stats.f_frsize * stats.f_bavail if self._card_a_prefix is not None: stats = os.statvfs(self._card_a_prefix) casz = stats.f_frsize * stats.f_bavail if self._card_b_prefix is not None: stats = os.statvfs(self._card_b_prefix) cbsz = stats.f_frsize * stats.f_bavail else: msz = self._windows_space(self._main_prefix)[1] casz = self._windows_space(self._card_a_prefix)[1] cbsz = self._windows_space(self._card_b_prefix)[1]

return (msz, casz, cbsz)

def windows_filter_pnp_id(self, pnp_id): return False

[belgeler] def windows_sort_drives(self, drives): ''' Called to disambiguate main memory and storage card for devices that do not distinguish between them on the basis of `WINDOWS_CARD_NAME`. For example: The EB600 ''' return drives

[belgeler] def can_handle_windows(self, usbdevice, debug=False): if hasattr(self.can_handle, 'is_base_class_implementation'): # No custom can_handle implementation return True # Delegate to the unix can_handle function, creating a unix like # USBDevice object from calibre.devices.winusb import get_usb_info dev = usb_info_cache.get(usbdevice) if dev is None: try: data = get_usb_info(usbdevice, debug=debug) except Exception: time.sleep(0.1) try: data = get_usb_info(usbdevice, debug=debug) except Exception: data = {} dev = usb_info_cache[usbdevice] = namedtuple( 'USBDevice', 'vendor_id product_id bcd manufacturer product serial')( usbdevice.vendor_id, usbdevice.product_id, usbdevice.bcd, data.get('manufacturer') or '', data.get('product') or '', data.get('serial_number') or '') if debug: prints(f'USB Info for device: {dev}') return self.can_handle(dev, debug=debug)

def open_windows(self): from calibre.devices.scanner import drive_is_ok from calibre.devices.winusb import get_drive_letters_for_device usbdev = self.device_being_opened debug = is_debugging() or getattr(self, 'do_device_debug', False) try: dlmap = get_drive_letters_for_device(usbdev, debug=debug) except Exception: dlmap = {}

if not dlmap.get('drive_letters'): time.sleep(7) dlmap = get_drive_letters_for_device(usbdev, debug=debug)

if debug: from pprint import pformat prints(f'Drive letters for {usbdev}') prints(pformat(dlmap))

filtered = set() for dl in dlmap['drive_letters']: pnp_id = dlmap['pnp_id_map'][dl].upper() if dl in dlmap['readonly_drives']: filtered.add(dl) if debug: prints('Ignoring the drive %s as it is readonly' % dl) elif self.windows_filter_pnp_id(pnp_id): filtered.add(dl) if debug: prints(f'Ignoring the drive {dl} because of a PNP filter on {pnp_id}') elif not drive_is_ok(dl, debug=debug): filtered.add(dl) if debug: prints('Ignoring the drive %s because failed to get free space for it' % dl) dlmap['drive_letters'] = [dl for dl in dlmap['drive_letters'] if dl not in filtered]

if not dlmap['drive_letters']: raise DeviceError(_('Unable to detect any disk drives for the device: %s. Try rebooting') % self.get_gui_name())

drives = {}

for drive_letter, which in zip(dlmap['drive_letters'], 'main carda cardb'.split()): drives[which] = drive_letter + ':\\'

drives = self.windows_sort_drives(drives) self._main_prefix = drives.get('main') self._card_a_prefix = drives.get('carda', None) self._card_b_prefix = drives.get('cardb', None)

@classmethod def run_ioreg(cls, raw=None): if raw is not None: return raw ioreg = '/usr/sbin/ioreg' if not os.access(ioreg, os.X_OK): ioreg = 'ioreg' cmd = (ioreg+' -w 0 -S -c IOMedia').split() for i in range(3): try: return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] except OSError: # Probably an interrupted system call if i == 2: raise time.sleep(2)

def osx_sort_names(self, names): return names

@classmethod def osx_run_mount(cls): for i in range(3): try: return subprocess.Popen('mount', stdout=subprocess.PIPE).communicate()[0] except OSError: # Probably an interrupted system call if i == 2: raise time.sleep(2)

@classmethod def osx_get_usb_drives(cls): from calibre_extensions.usbobserver import get_usb_drives return get_usb_drives()

def _osx_bsd_names(self): drives = self.osx_get_usb_drives() matches = [] d = self.detected_device if d.serial: for path, vid, pid, bcd, ven, prod, serial in drives: if d.match_serial(serial): matches.append(path) if not matches and d.manufacturer and d.product: for path, vid, pid, bcd, man, prod, serial in drives: if d.match_strings(vid, pid, bcd, man, prod): matches.append(path) if not matches: # Since Apple started mangling the names stored in the IOKit # registry, we cannot trust match_strings() so fallback to matching # on just numbers. See http://www.mobileread.com/forums/showthread.php?t=273213 for path, vid, pid, bcd, man, prod, serial in drives: if d.match_numbers(vid, pid, bcd): matches.append(path) if not matches: from pprint import pformat raise DeviceError( f'Could not detect BSD names for {self.name}. Try rebooting.\nOutput from osx_get_usb_drives():\n{pformat(drives)}')

pat = re.compile(r'(?P<m>\d+)([a-z]+(?P<p>\d+)){0,1}')

def nums(x): 'Return (disk num, partition number)' m = pat.search(x) if m is None: return (10000, -1) g = m.groupdict() if g['p'] is None: g['p'] = 0 return list(map(int, (g.get('m'), g.get('p'))))

def cmp_key(x): ''' Sorting based on the following scheme: - disks without partitions are first - sub sorted based on disk number - disks with partitions are sorted first on disk number, then on partition number ''' x = x.rpartition('/')[-1] disk_num, part_num = nums(x) has_part = 1 if part_num > 0 else 0 return has_part, disk_num, part_num matches.sort(key=cmp_key) drives = {'main':matches[0]} if len(matches) > 1: drives['carda'] = matches[1] if len(matches) > 2: drives['cardb'] = matches[2]

return drives def osx_bsd_names(self): drives = {} for i in range(3): try: drives = self._osx_bsd_names() if len(drives) > 1: # wait for device to settle and SD card (if any) to become available return drives except Exception: if i == 2: raise time.sleep(3) return drives def open_osx(self): from calibre_extensions.usbobserver import get_mounted_filesystems bsd_drives = self.osx_bsd_names() drives = self.osx_sort_names(bsd_drives.copy()) mount_map = get_mounted_filesystems() # macOS 13 Ventura uses a weird scheme for mounted FAT devices of the # form fat://basename_of_bsd_name/basename_of_mountpoint # see https://www.mobileread.com/forums/showthread.php?t=347294 for dev_node in tuple(mount_map): if ':' in dev_node and '//' in dev_node: val = mount_map[dev_node] dev_node = dev_node.split('/')[-2] dev_node = f'/dev/{dev_node}' if dev_node not in mount_map: mount_map[dev_node] = val drives = {k: mount_map.get(v) for k, v in iteritems(drives)} if is_debugging(): print() from pprint import pprint pprint({'bsd_drives': bsd_drives, 'mount_map': mount_map, 'drives': drives}) if drives.get('carda') is None and drives.get('cardb') is not None: drives['carda'] = drives.pop('cardb') if drives.get('main') is None and drives.get('carda') is not None: drives['main'] = drives.pop('carda') if drives.get('carda') is None and drives.get('cardb') is not None: drives['carda'] = drives.pop('cardb') if drives.get('main') is None: raise DeviceError(_('Unable to detect the %s mount point. Try rebooting.')%self.__class__.__name__) pat = self.OSX_MAIN_MEM_VOL_PAT if pat is not None and len(drives) > 1 and 'main' in drives: if pat.search(drives['main']) is None: main = drives['main'] for x in ('carda', 'cardb'): if x in drives and pat.search(drives[x]): drives['main'] = drives.pop(x) drives[x] = main break self._main_prefix = drives['main']+os.sep def get_card_prefix(c): ans = drives.get(c, None) if ans is not None: ans += os.sep return ans self._card_a_prefix = get_card_prefix('carda') self._card_b_prefix = get_card_prefix('cardb')

def find_device_nodes(self, detected_device=None):

def walk(base): base = os.path.abspath(os.path.realpath(base)) for x in os.listdir(base): p = os.path.join(base, x) if os.path.islink(p) or not os.access(p, os.R_OK): continue isfile = os.path.isfile(p) yield p, isfile if not isfile: yield from walk(p)

def raw2num(raw): raw = raw.lower() if not raw.startswith('0x'): raw = '0x' + raw return int(raw, 16)

# Find device node based on vendor, product and bcd d, j = os.path.dirname, os.path.join usb_dir = None if detected_device is None: detected_device = self.detected_device def test(val, attr): q = getattr(detected_device, attr) return q == val def getnum(usb_dir): def rc(q): with open(j(usb_dir, q), 'rb') as f: return raw2num(f.read().decode('utf-8')) return rc for x, isfile in walk('/sys/devices'): if isfile and x.endswith('idVendor'): usb_dir = d(x) for y in ('idProduct', 'idVendor', 'bcdDevice'): if not os.access(j(usb_dir, y), os.R_OK): usb_dir = None break if usb_dir is None: continue ven, prod, bcd = map(getnum(usb_dir), ('idVendor', 'idProduct', 'bcdDevice')) if not (test(ven, 'idVendor') and test(prod, 'idProduct') and test(bcd, 'bcdDevice')): usb_dir = None continue else: break if usb_dir is None: raise DeviceError(_('Unable to detect the %s disk drive.') %self.__class__.__name__)

devnodes, ok = [], {} for x, isfile in walk(usb_dir): if not isfile and '/block/' in x: parts = x.split('/') idx = parts.index('block') if idx == len(parts)-2: sz = j(x, 'size') node = parts[idx+1] try: with open(sz, 'rb') as szf: exists = int(szf.read().decode('utf-8')) > 0 if exists: node = self.find_largest_partition(x) ok[node] = True else: ok[node] = False except: ok[node] = False if is_debugging() and not ok[node]: print(f'\nIgnoring the node: {node} as could not read size from: {sz}')

devnodes.append(node)

devnodes += list(repeat(None, 3)) ans = ['/dev/'+x if ok.get(x, False) else None for x in devnodes] ans.sort(key=lambda x: x[5:] if x else 'zzzzz') return self.linux_swap_drives(ans[:3])

def linux_swap_drives(self, drives): return drives def node_mountpoint(self, node): from calibre.devices.udisks import node_mountpoint return node_mountpoint(node)

def find_largest_partition(self, path): node = path.split('/')[-1] nodes = [] for x in glob.glob(path+'/'+node+'*'): sz = x + '/size'

if not os.access(sz, os.R_OK): continue try: with open(sz, 'rb') as szf: sz = int(szf.read().decode('utf-8')) except: continue if sz > 0: nodes.append((x.split('/')[-1], sz))

nodes.sort(key=lambda x: x[1]) if not nodes: return node return nodes[-1][0]

def open_linux(self):

def mount(node, type): mp = self.node_mountpoint(node) if mp is not None: return mp, 0 def do_mount(node): try: from calibre.devices.udisks import mount mount(node) return 0 except: print('Udisks mount call failed:') import traceback traceback.print_exc() return 1 ret = do_mount(node) if ret != 0: return None, ret return self.node_mountpoint(node)+'/', 0 main, carda, cardb = self.find_device_nodes() if main is None: raise DeviceError(_('Unable to detect the %s disk drive. Either ' 'the device has already been ejected, or your ' 'kernel is exporting a deprecated version of SYSFS.') %self.__class__.__name__) if is_debugging(): print('\nFound device nodes:', main, carda, cardb)

self._linux_mount_map = {} mp, ret = mount(main, 'main') if mp is None: raise DeviceError( _('Unable to mount main memory (Error code: %d)')%ret) if not mp.endswith('/'): mp += '/' self._linux_mount_map[main] = mp self._main_prefix = mp self._linux_main_device_node = main cards = [(carda, '_card_a_prefix', 'carda'), (cardb, '_card_b_prefix', 'cardb')] for card, prefix, typ in cards: if card is None: continue mp, ret = mount(card, typ) if mp is None: print('Unable to mount card (Error code: %d)'%ret, file=sys.stderr) else: if not mp.endswith('/'): mp += '/' setattr(self, prefix, mp) self._linux_mount_map[card] = mp self.filter_read_only_mount_points()

def filter_read_only_mount_points(self):

def is_readonly(mp): if mp is None: return True path = os.path.join(mp, 'calibre_readonly_test') ro = True try: with open(path, 'wb'): ro = False except: pass else: try: os.remove(path) except: pass if is_debugging() and ro: print('\nThe mountpoint', mp, 'is readonly, ignoring it') return ro for mp in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'): if is_readonly(getattr(self, mp, None)): setattr(self, mp, None)

if self._main_prefix is None: for p in ('_card_a_prefix', '_card_b_prefix'): nmp = getattr(self, p, None) if nmp is not None: self._main_prefix = nmp setattr(self, p, None) break if self._main_prefix is None: raise DeviceError(_('The main memory of %s is read only. ' 'This usually happens because of file system errors.') %self.__class__.__name__)

if self._card_a_prefix is None and self._card_b_prefix is not None: self._card_a_prefix = self._card_b_prefix self._card_b_prefix = None

# ------------------------------------------------------ # # open for FreeBSD # find the device node or nodes that match the S/N we already have from the scanner # and attempt to mount each one # 1. get list of devices in /dev with matching s/n etc. # 2. get list of volumes associated with each # 3. attempt to mount each one using Hal # 4. when finished, we have a list of mount points and associated dbus nodes # def open_freebsd(self): # There should be some way to access the -v arg... verbose = False

# this gives us access to the S/N, etc. of the reader that the scanner has found # and the match routines for some of that data, like s/n, vendor ID, etc. d=self.detected_device if not d.serial: raise DeviceError("Device has no S/N. Can't continue") from .hal import get_hal hal = get_hal() vols = hal.get_volumes(d) if verbose: print("FBSD: ", vols)

ok, mv = hal.mount_volumes(vols) if not ok: raise DeviceError(_('Unable to mount the device')) for k, v in mv.items(): setattr(self, k, v)

# # ------------------------------------------------------ # # this one is pretty simple: # just umount each of the previously # mounted filesystems, using the stored volume object # def eject_freebsd(self): from .hal import get_hal hal = get_hal() if self._main_prefix: hal.unmount(self._main_vol) if self._card_a_prefix: hal.unmount(self._card_a_vol) if self._card_b_prefix: hal.unmount(self._card_b_vol)

self._main_prefix = self._main_vol = None self._card_a_prefix = self._card_a_vol = None self._card_b_prefix = self._card_b_vol = None # ------------------------------------------------------

[belgeler] def open(self, connected_device, library_uuid): time.sleep(5) self._main_prefix = self._card_a_prefix = self._card_b_prefix = None self.device_being_opened = connected_device try: if islinux: try: self.open_linux() except DeviceError: time.sleep(7) self.open_linux() if isfreebsd: self._main_vol = self._card_a_vol = self._card_b_vol = None try: self.open_freebsd() except DeviceError: time.sleep(2) self.open_freebsd() if iswindows: self.open_windows() if ismacos: try: self.open_osx() except DeviceError: time.sleep(7) self.open_osx()

self.current_library_uuid = library_uuid self.post_open_callback() finally: self.device_being_opened = None
def post_open_callback(self): pass def eject_windows(self): from threading import Thread drives = [] for x in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'): x = getattr(self, x, None) if x is not None: drives.append(x[0].upper())

def do_it(drives): subprocess.Popen([eject_exe()] + drives, creationflags=subprocess.CREATE_NO_WINDOW).wait()

t = Thread(target=do_it, args=[drives]) t.daemon = True t.start() self.__save_win_eject_thread = t def eject_osx(self): for x in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'): x = getattr(self, x, None) if x is not None: try: subprocess.Popen(self.OSX_EJECT_COMMAND + [x]) except: pass def eject_linux(self): from calibre.devices.udisks import eject, umount drives = [d for d in self.find_device_nodes() if d] for d in drives: try: umount(d) except: pass for d in drives: try: eject(d) except Exception as e: print('Udisks eject call for:', d, 'failed:') print('\t', e)

[belgeler] def eject(self): if islinux: try: self.eject_linux() except: pass if isfreebsd: try: self.eject_freebsd() except: pass if iswindows: try: self.eject_windows() except: pass if ismacos: try: self.eject_osx() except: pass self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
def linux_post_yank(self): self._linux_mount_map = {}

[belgeler] def post_yank_cleanup(self): if islinux: try: self.linux_post_yank() except: import traceback traceback.print_exc() self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
def get_main_ebook_dir(self, for_upload=False): return self.EBOOK_DIR_MAIN def get_carda_ebook_dir(self, for_upload=False): return self.EBOOK_DIR_CARD_A def get_cardb_ebook_dir(self, for_upload=False): return self.EBOOK_DIR_CARD_B def _sanity_check(self, on_card, files): from calibre.devices.utils import sanity_check sanity_check(on_card, files, self.card_prefix(), self.free_space())

def get_dest_dir(prefix, candidates): if isinstance(candidates, string_or_bytes): candidates = [candidates] if not candidates: candidates = [''] candidates = [ ((os.path.join(prefix, *(x.split('/')))) if x else prefix) for x in candidates] existing = [x for x in candidates if os.path.exists(x)] if not existing: existing = candidates return existing[0]

if on_card == 'carda': candidates = self.get_carda_ebook_dir(for_upload=True) path = get_dest_dir(self._card_a_prefix, candidates) elif on_card == 'cardb': candidates = self.get_cardb_ebook_dir(for_upload=True) path = get_dest_dir(self._card_b_prefix, candidates) else: candidates = self.get_main_ebook_dir(for_upload=True) path = get_dest_dir(self._main_prefix, candidates)

return path

[belgeler] def sanitize_callback(self, path): ''' Callback to allow individual device drivers to override the path sanitization used by :meth:`create_upload_path`. ''' return sanitize(path)

[belgeler] def filename_callback(self, default, mi): ''' Callback to allow drivers to change the default file name set by :meth:`create_upload_path`. ''' return default

[belgeler] def sanitize_path_components(self, components): ''' Perform any device specific sanitization on the path components for files to be uploaded to the device ''' return components

[belgeler] def get_annotations(self, path_map): ''' Resolve path_map to annotation_map of files found on the device ''' return {}

[belgeler] def add_annotation_to_library(self, db, db_id, annotation): ''' Add an annotation to the calibre library ''' pass
def create_upload_path(self, path, mdata, fname, create_dirs=True): from calibre.devices.utils import create_upload_path settings = self.settings() filepath = create_upload_path(mdata, fname, self.save_template(), self.sanitize_callback, prefix_path=os.path.abspath(path), maxlen=self.MAX_PATH_LEN, use_subdirs=self.SUPPORTS_SUB_DIRS and settings.use_subdirs, news_in_folder=self.NEWS_IN_FOLDER, filename_callback=self.filename_callback, sanitize_path_components=self.sanitize_path_components ) filedir = os.path.dirname(filepath)

if create_dirs and not os.path.exists(filedir): os.makedirs(filedir)

return filepath def create_annotations_path(self, mdata, device_path=None): return self.create_upload_path(os.path.abspath('/<storage>'), mdata, 'x.bookmark', create_dirs=False)

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.devices.usbms.driver

calibre.devices.usbms.driver öğesinin kaynak kodu

__license__ = 'GPL v3' __copyright__ = '2009, John Schember <john at nachtimwald.com>' __docformat__ = 'restructuredtext en'

''' Generic USB Mass storage device driver. This is not a complete stand alone driver. It is intended to be subclassed with the relevant parts implemented for a particular device. '''

import json import os import shutil import time from itertools import cycle from calibre import fsync, isbytestring, prints from calibre.constants import filesystem_encoding, is_debugging, ismacos, numeric_version from calibre.devices.usbms.books import Book, BookList from calibre.devices.usbms.cli import CLI from calibre.devices.usbms.device import Device from calibre.ebooks.metadata.book.json_codec import JsonCodec from polyglot.builtins import itervalues, string_or_bytes def debug_print(*args, **kw): base_time = getattr(debug_print, 'base_time', None) if base_time is None: debug_print.base_time = base_time = time.monotonic() if is_debugging(): prints('DEBUG: %6.1f'%(time.monotonic()-base_time), *args, **kw)

def safe_walk(top, topdown=True, onerror=None, followlinks=False, maxdepth=128): ' A replacement for os.walk that does not die when it encounters undecodeable filenames in a linux filesystem' if maxdepth < 0: return islink, join, isdir = os.path.islink, os.path.join, os.path.isdir

# We may not have read permission for top, in which case we can't # get a list of the files the directory contains. os.path.walk # always suppressed the exception then, rather than blow up for a # minor reason when (say) a thousand readable directories are still # left to visit. That logic is copied here. try: names = os.listdir(top) except OSError as err: if onerror is not None: onerror(err) return dirs, nondirs = [], [] for name in names: if isinstance(name, bytes): try: name = name.decode(filesystem_encoding) except UnicodeDecodeError: debug_print('Skipping undecodeable file: %r' % name) continue if isdir(join(top, name)): dirs.append(name) else: nondirs.append(name)

if topdown: yield top, dirs, nondirs for name in dirs: new_path = join(top, name) if followlinks or not islink(new_path): yield from safe_walk(new_path, topdown, onerror, followlinks, maxdepth-1) if not topdown: yield top, dirs, nondirs

# CLI must come before Device as it implements the CLI functions that # are inherited from the device interface in Device. [belgeler] class USBMS(CLI, Device):

''' The base class for all USBMS devices. Implements the logic for sending/getting/updating metadata/caching metadata/etc. '''

description = _('Communicate with an e-book reader.') author = 'John Schember' supported_platforms = ['windows', 'osx', 'linux']

# Store type instances of BookList and Book. We must do this because # a) we need to override these classes in some device drivers, and # b) the classmethods seem only to see real attributes declared in the # class, not attributes stored in the class booklist_class = BookList book_class = Book

FORMATS = [] CAN_SET_METADATA = [] METADATA_CACHE = 'metadata.calibre' DRIVEINFO = 'driveinfo.calibre'

SCAN_FROM_ROOT = False def _update_driveinfo_record(self, dinfo, prefix, location_code, name=None): import uuid from calibre.utils.date import isoformat, now if not isinstance(dinfo, dict): dinfo = {} if dinfo.get('device_store_uuid', None) is None: dinfo['device_store_uuid'] = str(uuid.uuid4()) if dinfo.get('device_name', None) is None: dinfo['device_name'] = self.get_gui_name() if name is not None: dinfo['device_name'] = name dinfo['location_code'] = location_code dinfo['last_library_uuid'] = getattr(self, 'current_library_uuid', None) dinfo['calibre_version'] = '.'.join([str(i) for i in numeric_version]) dinfo['date_last_connected'] = isoformat(now()) dinfo['prefix'] = prefix.replace('\\', '/') return dinfo def _update_driveinfo_file(self, prefix, location_code, name=None): from calibre.utils.config import from_json, to_json if os.path.exists(os.path.join(prefix, self.DRIVEINFO)): with open(os.path.join(prefix, self.DRIVEINFO), 'rb') as f: try: driveinfo = json.loads(f.read(), object_hook=from_json) except: driveinfo = None driveinfo = self._update_driveinfo_record(driveinfo, prefix, location_code, name) data = json.dumps(driveinfo, default=to_json) if not isinstance(data, bytes): data = data.encode('utf-8') with open(os.path.join(prefix, self.DRIVEINFO), 'wb') as f: f.write(data) fsync(f) else: driveinfo = self._update_driveinfo_record({}, prefix, location_code, name) data = json.dumps(driveinfo, default=to_json) if not isinstance(data, bytes): data = data.encode('utf-8') with open(os.path.join(prefix, self.DRIVEINFO), 'wb') as f: f.write(data) fsync(f) return driveinfo

[belgeler] def get_device_information(self, end_session=True): self.report_progress(1.0, _('Get device information...')) self.driveinfo = {}

def raise_os_error(e): raise OSError(_('Failed to access files in the main memory of' ' your device. You should contact the device' ' manufacturer for support. Common fixes are:' ' try a different USB cable/USB port on your computer.' ' If you device has a "Reset to factory defaults" type' ' of setting somewhere, use it. Underlying error: %s') % e) from e if self._main_prefix is not None: try: self.driveinfo['main'] = self._update_driveinfo_file(self._main_prefix, 'main') except PermissionError as e: if ismacos: raise PermissionError(_( 'Permission was denied by macOS trying to access files in the main memory of' ' your device. You will need to grant permission explicitly by looking under' ' System Preferences > Security and Privacy > Privacy > Files and Folders.' ' Underlying error: %s' ) % e) from e raise_os_error(e) except OSError as e: raise_os_error(e) try: if self._card_a_prefix is not None: self.driveinfo['A'] = self._update_driveinfo_file(self._card_a_prefix, 'A') if self._card_b_prefix is not None: self.driveinfo['B'] = self._update_driveinfo_file(self._card_b_prefix, 'B') except OSError as e: raise OSError(_('Failed to access files on the SD card in your' ' device. This can happen for many reasons. The SD card may be' ' corrupted, it may be too large for your device, it may be' ' write-protected, etc. Try a different SD card, or reformat' ' your SD card using the FAT32 filesystem. Also make sure' ' there are not too many files in the root of your SD card.' ' Underlying error: %s') % e) return (self.get_gui_name(), '', '', '', self.driveinfo)

[belgeler] def set_driveinfo_name(self, location_code, name): if location_code == 'main': self._update_driveinfo_file(self._main_prefix, location_code, name) elif location_code == 'A': self._update_driveinfo_file(self._card_a_prefix, location_code, name) elif location_code == 'B': self._update_driveinfo_file(self._card_b_prefix, location_code, name)

def formats_to_scan_for(self): return set(self.settings().format_map) | set(self.FORMATS)

def is_allowed_book_file(self, filename, path, prefix): return True

[belgeler] def books(self, oncard=None, end_session=True): from calibre.ebooks.metadata.meta import path_to_ext debug_print('USBMS: Fetching list of books from device. Device=', self.__class__.__name__, 'oncard=', oncard)

dummy_bl = self.booklist_class(None, None, None)

if oncard == 'carda' and not self._card_a_prefix: self.report_progress(1.0, _('Getting list of books on device...')) return dummy_bl elif oncard == 'cardb' and not self._card_b_prefix: self.report_progress(1.0, _('Getting list of books on device...')) return dummy_bl elif oncard and oncard != 'carda' and oncard != 'cardb': self.report_progress(1.0, _('Getting list of books on device...')) return dummy_bl prefix = self._card_a_prefix if oncard == 'carda' else \ self._card_b_prefix if oncard == 'cardb' \ else self._main_prefix ebook_dirs = self.get_carda_ebook_dir() if oncard == 'carda' else \ self.EBOOK_DIR_CARD_B if oncard == 'cardb' else \ self.get_main_ebook_dir()

debug_print('USBMS: dirs are:', prefix, ebook_dirs)

# get the metadata cache bl = self.booklist_class(oncard, prefix, self.settings) need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE)

# make a dict cache of paths so the lookup in the loop below is faster. bl_cache = {} for idx, b in enumerate(bl): bl_cache[b.lpath] = idx all_formats = self.formats_to_scan_for()

def update_booklist(filename, path, prefix): changed = False # Ignore AppleDouble files if filename.startswith("._"): return False if path_to_ext(filename) in all_formats and self.is_allowed_book_file(filename, path, prefix): try: lpath = os.path.join(path, filename).partition(self.normalize_path(prefix))[2] if lpath.startswith(os.sep): lpath = lpath[len(os.sep):] lpath = lpath.replace('\\', '/') idx = bl_cache.get(lpath, None) if idx is not None: bl_cache[lpath] = None if self.update_metadata_item(bl[idx]): # print 'update_metadata_item returned true' changed = True else: if bl.add_book(self.book_from_path(prefix, lpath), replace_metadata=False): changed = True except: # Probably a filename encoding error import traceback traceback.print_exc() return changed if isinstance(ebook_dirs, string_or_bytes): ebook_dirs = [ebook_dirs] for ebook_dir in ebook_dirs: ebook_dir = self.path_to_unicode(ebook_dir) if self.SCAN_FROM_ROOT: ebook_dir = self.normalize_path(prefix) else: ebook_dir = self.normalize_path( os.path.join(prefix, *(ebook_dir.split('/'))) if ebook_dir else prefix) debug_print('USBMS: scan from root', self.SCAN_FROM_ROOT, ebook_dir) if not os.path.exists(ebook_dir): continue # Get all books in the ebook_dir directory if self.SUPPORTS_SUB_DIRS or self.SUPPORTS_SUB_DIRS_FOR_SCAN: # build a list of files to check, so we can accurately report progress flist = [] for path, dirs, files in safe_walk(ebook_dir): for filename in files: if filename != self.METADATA_CACHE: flist.append({'filename': self.path_to_unicode(filename), 'path':self.path_to_unicode(path)}) for i, f in enumerate(flist): self.report_progress(i/float(len(flist)), _('Getting list of books on device...')) changed = update_booklist(f['filename'], f['path'], prefix) if changed: need_sync = True else: paths = os.listdir(ebook_dir) for i, filename in enumerate(paths): self.report_progress((i+1) / float(len(paths)), _('Getting list of books on device...')) changed = update_booklist(self.path_to_unicode(filename), ebook_dir, prefix) if changed: need_sync = True

# Remove books that are no longer in the filesystem. Cache contains # indices into the booklist if book not in filesystem, None otherwise # Do the operation in reverse order so indices remain valid for idx in sorted(itervalues(bl_cache), reverse=True, key=lambda x: -1 if x is None else x): if idx is not None: need_sync = True del bl[idx]

debug_print('USBMS: count found in cache: %d, count of files in metadata: %d, need_sync: %s' % (len(bl_cache), len(bl), need_sync)) if need_sync: # self.count_found_in_bl != len(bl) or need_sync: if oncard == 'cardb': self.sync_booklists((None, None, bl)) elif oncard == 'carda': self.sync_booklists((None, bl, None)) else: self.sync_booklists((bl, None, None))

self.report_progress(1.0, _('Getting list of books on device...')) debug_print('USBMS: Finished fetching list of books from device. oncard=', oncard) return bl[belgeler] def upload_books(self, files, names, on_card=None, end_session=True, metadata=None): debug_print('USBMS: uploading %d books'%(len(files)))

path = self._sanity_check(on_card, files)

paths = [] names = iter(names) metadata = iter(metadata)

for i, infile in enumerate(files): mdata, fname = next(metadata), next(names) filepath = self.normalize_path(self.create_upload_path(path, mdata, fname)) if not hasattr(infile, 'read'): infile = self.normalize_path(infile) filepath = self.put_file(infile, filepath, replace_file=True) paths.append(filepath) try: self.upload_cover(os.path.dirname(filepath), os.path.splitext(os.path.basename(filepath))[0], mdata, filepath) except: # Failure to upload cover is not catastrophic import traceback traceback.print_exc()

self.report_progress((i+1) / float(len(files)), _('Transferring books to device...'))

self.report_progress(1.0, _('Transferring books to device...')) debug_print('USBMS: finished uploading %d books'%(len(files))) return list(zip(paths, cycle([on_card])))

[belgeler] def upload_cover(self, path, filename, metadata, filepath): ''' Upload book cover to the device. Default implementation does nothing.

:param path: The full path to the folder where the associated book is located. :param filename: The name of the book file without the extension. :param metadata: metadata belonging to the book. Use metadata.thumbnail for cover :param filepath: The full path to the e-book file

''' pass

[belgeler] def add_books_to_metadata(self, locations, metadata, booklists): debug_print('USBMS: adding metadata for %d books'%(len(metadata)))

metadata = iter(metadata) locations = tuple(locations) for i, location in enumerate(locations): self.report_progress((i+1) / float(len(locations)), _('Adding books to device metadata listing...')) info = next(metadata) blist = 2 if location[1] == 'cardb' else 1 if location[1] == 'carda' else 0

# Extract the correct prefix from the pathname. To do this correctly, # we must ensure that both the prefix and the path are normalized # so that the comparison will work. Book's __init__ will fix up # lpath, so we don't need to worry about that here. path = self.normalize_path(location[0]) if self._main_prefix: prefix = self._main_prefix if \ path.startswith(self.normalize_path(self._main_prefix)) else None if not prefix and self._card_a_prefix: prefix = self._card_a_prefix if \ path.startswith(self.normalize_path(self._card_a_prefix)) else None if not prefix and self._card_b_prefix: prefix = self._card_b_prefix if \ path.startswith(self.normalize_path(self._card_b_prefix)) else None if prefix is None: prints('in add_books_to_metadata. Prefix is None!', path, self._main_prefix) continue lpath = path.partition(prefix)[2] if lpath.startswith('/') or lpath.startswith('\\'): lpath = lpath[1:] book = self.book_class(prefix, lpath, other=info) if book.size is None: book.size = os.stat(self.normalize_path(path)).st_size b = booklists[blist].add_book(book, replace_metadata=True) if b: b._new_book = True self.report_progress(1.0, _('Adding books to device metadata listing...')) debug_print('USBMS: finished adding metadata')def delete_single_book(self, path): os.unlink(path)

def delete_extra_book_files(self, path): filepath = os.path.splitext(path)[0] for ext in self.DELETE_EXTS: for x in (filepath, path): x += ext if os.path.exists(x): if os.path.isdir(x): shutil.rmtree(x, ignore_errors=True) else: os.unlink(x)

if self.SUPPORTS_SUB_DIRS: try: os.removedirs(os.path.dirname(path)) except: pass

[belgeler] def delete_books(self, paths, end_session=True): debug_print('USBMS: deleting %d books'%(len(paths))) for i, path in enumerate(paths): self.report_progress((i+1) / float(len(paths)), _('Removing books from device...')) path = self.normalize_path(path) if os.path.exists(path): # Delete the ebook self.delete_single_book(path) self.delete_extra_book_files(path)

self.report_progress(1.0, _('Removing books from device...')) debug_print('USBMS: finished deleting %d books'%(len(paths)))

[belgeler] def remove_books_from_metadata(self, paths, booklists): debug_print('USBMS: removing metadata for %d books'%(len(paths)))

for i, path in enumerate(paths): self.report_progress((i+1) / float(len(paths)), _('Removing books from device metadata listing...')) for bl in booklists: for book in bl: if path.endswith(book.path): bl.remove_book(book) self.report_progress(1.0, _('Removing books from device metadata listing...')) debug_print('USBMS: finished removing metadata for %d books'%(len(paths)))

# If you override this method and you use book._new_book, then you must # complete the processing before you call this method. The flag is cleared # at the end just before the return [belgeler] def sync_booklists(self, booklists, end_session=True): debug_print('USBMS: starting sync_booklists') json_codec = JsonCodec()

if not os.path.exists(self.normalize_path(self._main_prefix)): os.makedirs(self.normalize_path(self._main_prefix))

def write_prefix(prefix, listid): if (prefix is not None and len(booklists) > listid and isinstance(booklists[listid], self.booklist_class)): if not os.path.exists(prefix): os.makedirs(self.normalize_path(prefix)) with open(self.normalize_path(os.path.join(prefix, self.METADATA_CACHE)), 'wb') as f: json_codec.encode_to_file(f, booklists[listid]) fsync(f) write_prefix(self._main_prefix, 0) write_prefix(self._card_a_prefix, 1) write_prefix(self._card_b_prefix, 2)

# Clear the _new_book indication, as we are supposed to be done with # adding books at this point for blist in booklists: if blist is not None: for book in blist: book._new_book = False self.report_progress(1.0, _('Sending metadata to device...')) debug_print('USBMS: finished sync_booklists')@classmethod def build_template_regexp(cls): from calibre.devices.utils import build_template_regexp return build_template_regexp(cls.save_template())

@classmethod def path_to_unicode(cls, path): if isbytestring(path): path = path.decode(filesystem_encoding) return path

[belgeler] @classmethod def normalize_path(cls, path): 'Return path with platform native path separators' if path is None: return None if os.sep == '\\': path = path.replace('/', '\\') else: path = path.replace('\\', '/') return cls.path_to_unicode(path)

@classmethod def parse_metadata_cache(cls, bl, prefix, name): json_codec = JsonCodec() need_sync = False cache_file = cls.normalize_path(os.path.join(prefix, name)) if os.access(cache_file, os.R_OK): try: with open(cache_file, 'rb') as f: json_codec.decode_from_file(f, bl, cls.book_class, prefix) except: import traceback traceback.print_exc() bl = [] need_sync = True else: need_sync = True return need_sync

@classmethod def update_metadata_item(cls, book): changed = False size = os.stat(cls.normalize_path(book.path)).st_size if size != book.size: changed = True mi = cls.metadata_from_path(book.path) book.smart_update(mi) book.size = size return changed

@classmethod def metadata_from_path(cls, path): return cls.metadata_from_formats([path])

@classmethod def metadata_from_formats(cls, fmts): from calibre.customize.ui import quick_metadata from calibre.ebooks.metadata.meta import metadata_from_formats with quick_metadata: return metadata_from_formats(fmts, force_read_metadata=True, pattern=cls.build_template_regexp())

@classmethod def book_from_path(cls, prefix, lpath): from calibre.ebooks.metadata.book.base import Metadata if cls.settings().read_metadata or cls.MUST_READ_METADATA: mi = cls.metadata_from_path(cls.normalize_path(os.path.join(prefix, lpath))) else: from calibre.ebooks.metadata.meta import metadata_from_filename mi = metadata_from_filename(cls.normalize_path(os.path.basename(lpath)), cls.build_template_regexp()) if mi is None: mi = Metadata(os.path.splitext(os.path.basename(lpath))[0], [_('Unknown')]) size = os.stat(cls.normalize_path(os.path.join(prefix, lpath))).st_size book = cls.book_class(prefix, lpath, other=mi, size=size) return book

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.metadata.book.base

calibre.ebooks.metadata.book.base öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import copy import traceback from calibre import prints from calibre.constants import DEBUG from calibre.ebooks.metadata.book import ALL_METADATA_FIELDS, SC_COPYABLE_FIELDS, SC_FIELDS_COPY_NOT_NULL, STANDARD_METADATA_FIELDS, TOP_LEVEL_IDENTIFIERS from calibre.library.field_metadata import FieldMetadata from calibre.utils.icu import lower as icu_lower from calibre.utils.icu import sort_key from calibre.utils.localization import ngettext from polyglot.builtins import iteritems, string_or_bytes

# Special sets used to optimize the performance of getting and setting # attributes on Metadata objects SIMPLE_GET = frozenset(STANDARD_METADATA_FIELDS - TOP_LEVEL_IDENTIFIERS) SIMPLE_SET = frozenset(SIMPLE_GET - {'identifiers'})

def human_readable(size, precision=2): """ Convert a size in bytes into megabytes """ ans = size/(1024*1024) if ans < 0.1: return '<0.1 MB' return ('%.'+str(precision)+'f'+ ' MB') % ans

NULL_VALUES = { 'user_metadata': {}, 'cover_data' : (None, None), 'tags' : [], 'identifiers' : {}, 'languages' : [], 'device_collections': [], 'author_sort_map': {}, 'authors' : [_('Unknown')], 'author_sort' : _('Unknown'), 'title' : _('Unknown'), 'user_categories' : {}, 'link_maps' : {}, 'language' : 'und' }

field_metadata = FieldMetadata()

def reset_field_metadata(): global field_metadata field_metadata = FieldMetadata()

def ck(typ): return icu_lower(typ).strip().replace(':', '').replace(',', '')

def cv(val): return val.strip().replace(',', '|')

[belgeler] class Metadata:

''' A class representing all the metadata for a book. The various standard metadata fields are available as attributes of this object. You can also stick arbitrary attributes onto this object.

Metadata from custom columns should be accessed via the get() method, passing in the lookup name for the column, for example: "#mytags".

Use the :meth:`is_null` method to test if a field is null.

This object also has functions to format fields into strings.

The list of standard metadata fields grows with time is in :data:`STANDARD_METADATA_FIELDS`.

Please keep the method based API of this class to a minimum. Every method becomes a reserved field name. ''' __calibre_serializable__ = True def __init__(self, title, authors=(_('Unknown'),), other=None, template_cache=None, formatter=None): ''' @param title: title or ``_('Unknown')`` @param authors: List of strings or [] @param other: None or a metadata object ''' _data = copy.deepcopy(NULL_VALUES) _data.pop('language') object.__setattr__(self, '_data', _data) if other is not None: self.smart_update(other) else: if title: self.title = title if authors: # List of strings or [] self.author = list(authors) if authors else [] # Needed for backward compatibility self.authors = list(authors) if authors else [] from calibre.ebooks.metadata.book.formatter import SafeFormat self.formatter = SafeFormat() if formatter is None else formatter self.template_cache = template_cache

[belgeler] def is_null(self, field): ''' Return True if the value of field is null in this object. 'null' means it is unknown or evaluates to False. So a title of _('Unknown') is null or a language of 'und' is null.

Be careful with numeric fields since this will return True for zero as well as None.

Also returns True if the field does not exist. ''' try: null_val = NULL_VALUES.get(field, None) val = getattr(self, field, None) return not val or val == null_val except: return True
def set_null(self, field): null_val = copy.copy(NULL_VALUES.get(field)) setattr(self, field, null_val)

def __getattribute__(self, field): _data = object.__getattribute__(self, '_data') if field in SIMPLE_GET: return _data.get(field, None) if field in TOP_LEVEL_IDENTIFIERS: return _data.get('identifiers').get(field, None) if field == 'language': try: return _data.get('languages', [])[0] except: return NULL_VALUES['language'] try: return object.__getattribute__(self, field) except AttributeError: pass if field in _data['user_metadata']: d = _data['user_metadata'][field] val = d['#value#'] if val is None and d['datatype'] == 'composite': d['#value#'] = 'RECURSIVE_COMPOSITE FIELD (Metadata) ' + field val = d['#value#'] = self.formatter.safe_format( d['display']['composite_template'], self, _('TEMPLATE ERROR'), self, column_name=field, template_cache=self.template_cache).strip() return val if field.startswith('#') and field.endswith('_index'): try: return self.get_extra(field[:-6]) except: pass raise AttributeError( 'Metadata object has no attribute named: '+ repr(field))

def __setattr__(self, field, val, extra=None): _data = object.__getattribute__(self, '_data') if field in SIMPLE_SET: if val is None: val = copy.copy(NULL_VALUES.get(field, None)) _data[field] = val elif field in TOP_LEVEL_IDENTIFIERS: field, val = self._clean_identifier(field, val) identifiers = _data['identifiers'] identifiers.pop(field, None) if val: identifiers[field] = val elif field == 'identifiers': if not val: val = copy.copy(NULL_VALUES.get('identifiers', None)) self.set_identifiers(val) elif field == 'language': langs = [] if val and val.lower() != 'und': langs = [val] _data['languages'] = langs elif field in _data['user_metadata']: d = _data['user_metadata'][field] d['#value#'] = val d['#extra#'] = extra else: # You are allowed to stick arbitrary attributes onto this object as # long as they don't conflict with global or user metadata names # Don't abuse this privilege self.__dict__[field] = val def __iter__(self): return iter(object.__getattribute__(self, '_data'))

def has_key(self, key): return key in object.__getattribute__(self, '_data')

def _evaluate_all_composites(self): custom_fields = object.__getattribute__(self, '_data')['user_metadata'] for field in custom_fields: self._evaluate_composite(field)

def _evaluate_composite(self, field): f = object.__getattribute__(self, '_data')['user_metadata'].get(field, None) if f is not None: if f['datatype'] == 'composite' and f['#value#'] is None: self.get(field)

[belgeler] def deepcopy(self, class_generator=lambda : Metadata(None)): ''' Do not use this method unless you know what you are doing, if you want to create a simple clone of this object, use :meth:`deepcopy_metadata` instead. Class_generator must be a function that returns an instance of Metadata or a subclass of it.''' # We don't need to evaluate all the composites here because we # are returning a "real" Metadata instance that has __get_attribute__. m = class_generator() if not isinstance(m, Metadata): return None object.__setattr__(m, '__dict__', copy.deepcopy(self.__dict__)) return m
def deepcopy_metadata(self): # We don't need to evaluate all the composites here because we # are returning a "real" Metadata instance that has __get_attribute__. m = Metadata(None) object.__setattr__(m, '_data', copy.deepcopy(object.__getattribute__(self, '_data'))) return m def get(self, field, default=None): try: return self.__getattribute__(field) except AttributeError: return default def get_extra(self, field, default=None): # Don't need to evaluate all composites because a composite can't have # an extra value _data = object.__getattribute__(self, '_data') if field in _data['user_metadata']: try: return _data['user_metadata'][field]['#extra#'] except: return default raise AttributeError( 'Metadata object has no attribute named: '+ repr(field))

def set(self, field, val, extra=None): self.__setattr__(field, val, extra)

[belgeler] def get_identifiers(self): ''' Return a copy of the identifiers dictionary. The dict is small, and the penalty for using a reference where a copy is needed is large. Also, we don't want any manipulations of the returned dict to show up in the book. ''' ans = object.__getattribute__(self, '_data')['identifiers'] if not ans: ans = {} return copy.deepcopy(ans)

def _clean_identifier(self, typ, val): if typ: typ = ck(typ) if val: val = cv(val) return typ, val

[belgeler] def set_identifiers(self, identifiers): ''' Set all identifiers. Note that if you previously set ISBN, calling this method will delete it. ''' cleaned = {ck(k):cv(v) for k, v in iteritems(identifiers) if k and v} object.__getattribute__(self, '_data')['identifiers'] = cleaned

[belgeler] def set_identifier(self, typ, val): 'If val is empty, deletes identifier of type typ' typ, val = self._clean_identifier(typ, val) if not typ: return identifiers = object.__getattribute__(self, '_data')['identifiers']

identifiers.pop(typ, None) if val: identifiers[typ] = val
def has_identifier(self, typ): identifiers = object.__getattribute__(self, '_data')['identifiers'] return typ in identifiers

# field-oriented interface. Intended to be the same as in LibraryDatabase

[belgeler] def standard_field_keys(self): ''' return a list of all possible keys, even if this book doesn't have them ''' return STANDARD_METADATA_FIELDS

[belgeler] def custom_field_keys(self): ''' return a list of the custom fields in this book ''' return iter(object.__getattribute__(self, '_data')['user_metadata'])

[belgeler] def all_field_keys(self): ''' All field keys known by this instance, even if their value is None ''' _data = object.__getattribute__(self, '_data') return frozenset(ALL_METADATA_FIELDS.union(frozenset(_data['user_metadata'])))

[belgeler] def metadata_for_field(self, key): ''' return metadata describing a standard or custom field. ''' if key not in self.custom_field_keys(): return self.get_standard_metadata(key, make_copy=False) return self.get_user_metadata(key, make_copy=False)

[belgeler] def all_non_none_fields(self): ''' Return a dictionary containing all non-None metadata fields, including the custom ones. ''' result = {} _data = object.__getattribute__(self, '_data') for attr in STANDARD_METADATA_FIELDS: v = _data.get(attr, None) if v is not None: result[attr] = v # separate these because it uses the self.get(), not _data.get() for attr in TOP_LEVEL_IDENTIFIERS: v = self.get(attr, None) if v is not None: result[attr] = v for attr in _data['user_metadata']: v = self.get(attr, None) if v is not None: result[attr] = v if _data['user_metadata'][attr]['datatype'] == 'series': result[attr+'_index'] = _data['user_metadata'][attr]['#extra#'] return result

# End of field-oriented interface

# Extended interfaces. These permit one to get copies of metadata dictionaries, and to # get and set custom field metadata

[belgeler] def get_standard_metadata(self, field, make_copy): ''' return field metadata from the field if it is there. Otherwise return None. field is the key name, not the label. Return a copy if requested, just in case the user wants to change values in the dict. ''' if field in field_metadata and field_metadata[field]['kind'] == 'field': if make_copy: return copy.deepcopy(field_metadata[field]) return field_metadata[field] return None

[belgeler] def get_all_standard_metadata(self, make_copy): ''' return a dict containing all the standard field metadata associated with the book. ''' if not make_copy: return field_metadata res = {} for k in field_metadata: if field_metadata[k]['kind'] == 'field': res[k] = copy.deepcopy(field_metadata[k]) return res

[belgeler] def get_all_user_metadata(self, make_copy): ''' return a dict containing all the custom field metadata associated with the book. ''' # Must evaluate all composites because we are returning a dict, not a # Metadata instance self._evaluate_all_composites() _data = object.__getattribute__(self, '_data') user_metadata = _data['user_metadata'] if not make_copy: return user_metadata res = {} for k in user_metadata: res[k] = copy.deepcopy(user_metadata[k]) return res

[belgeler] def get_user_metadata(self, field, make_copy): ''' return field metadata from the object if it is there. Otherwise return None. field is the key name, not the label. Return a copy if requested, just in case the user wants to change values in the dict. ''' _data = object.__getattribute__(self, '_data')['user_metadata'] if field in _data: # Must evaluate the field because it might be a composite. It won't # be evaluated on demand because we are returning its dict, not a # Metadata instance self._evaluate_composite(field) if make_copy: return copy.deepcopy(_data[field]) return _data[field] return None

[belgeler] def set_all_user_metadata(self, metadata): ''' store custom field metadata into the object. Field is the key name not the label ''' if metadata is None: traceback.print_stack() return um = {} for key, meta in iteritems(metadata): m = meta.copy() if '#value#' not in m: if m['datatype'] == 'text' and m['is_multiple']: m['#value#'] = [] else: m['#value#'] = None um[key] = m _data = object.__getattribute__(self, '_data') _data['user_metadata'] = um

[belgeler] def set_user_metadata(self, field, metadata): ''' store custom field metadata for one column into the object. Field is the key name not the label ''' if field is not None: if not field.startswith('#'): raise AttributeError( 'Custom field name %s must begin with \'#\''%repr(field)) if metadata is None: traceback.print_stack() return m = dict(metadata) # Copying the elements should not be necessary. The objects referenced # in the dict should not change. Of course, they can be replaced. # for k,v in iteritems(metadata): # m[k] = copy.copy(v) if '#value#' not in m: if m['datatype'] == 'text' and m['is_multiple']: m['#value#'] = [] else: m['#value#'] = None _data = object.__getattribute__(self, '_data') _data['user_metadata'][field] = m

[belgeler] def remove_stale_user_metadata(self, other_mi): ''' Remove user metadata keys (custom column keys) if they don't exist in 'other_mi', which must be a metadata object ''' me = self.get_all_user_metadata(make_copy=False) other = set(other_mi.custom_field_keys()) new = {} for k,v in me.items(): if k in other: new[k] = v self.set_all_user_metadata(new)

[belgeler] def template_to_attribute(self, other, ops): ''' Takes a list [(src,dest), (src,dest)], evaluates the template in the context of other, then copies the result to self[dest]. This is on a best-efforts basis. Some assignments can make no sense. ''' if not ops: return from calibre.ebooks.metadata.book.formatter import SafeFormat formatter = SafeFormat() for op in ops: try: src = op[0] dest = op[1] val = formatter.safe_format(src, other, 'PLUGBOARD TEMPLATE ERROR', other) if dest == 'tags': self.set(dest, [f.strip() for f in val.split(',') if f.strip()]) elif dest == 'authors': self.set(dest, [f.strip() for f in val.split('&') if f.strip()]) else: self.set(dest, val) except: if DEBUG: traceback.print_exc()

# Old Metadata API {{{ def print_all_attributes(self): for x in STANDARD_METADATA_FIELDS: prints('%s:'%x, getattr(self, x, 'None')) for x in self.custom_field_keys(): meta = self.get_user_metadata(x, make_copy=False) if meta is not None: prints(x, meta) prints('--------------')

[belgeler] def smart_update(self, other, replace_metadata=False): ''' Merge the information in `other` into self. In case of conflicts, the information in `other` takes precedence, unless the information in `other` is NULL. ''' def copy_not_none(dest, src, attr): v = getattr(src, attr, None) if v not in (None, NULL_VALUES.get(attr, None)): setattr(dest, attr, copy.deepcopy(v))

unknown = _('Unknown') if other.title and other.title != unknown: self.title = other.title if hasattr(other, 'title_sort'): self.title_sort = other.title_sort if other.authors and ( other.authors[0] != unknown or ( not self.authors or ( len(self.authors) == 1 and self.authors[0] == unknown and getattr(self, 'author_sort', None) == unknown ) ) ): self.authors = list(other.authors) if hasattr(other, 'author_sort_map'): self.author_sort_map = dict(other.author_sort_map) if hasattr(other, 'author_sort'): self.author_sort = other.author_sort if replace_metadata: # SPECIAL_FIELDS = frozenset(['lpath', 'size', 'comments', 'thumbnail']) for attr in SC_COPYABLE_FIELDS: setattr(self, attr, getattr(other, attr, 1.0 if attr == 'series_index' else None)) self.tags = other.tags self.cover_data = getattr(other, 'cover_data', NULL_VALUES['cover_data']) self.set_all_user_metadata(other.get_all_user_metadata(make_copy=True)) for x in SC_FIELDS_COPY_NOT_NULL: copy_not_none(self, other, x) if callable(getattr(other, 'get_identifiers', None)): self.set_identifiers(other.get_identifiers()) # language is handled below else: for attr in SC_COPYABLE_FIELDS: copy_not_none(self, other, attr) for x in SC_FIELDS_COPY_NOT_NULL: copy_not_none(self, other, x)

if other.tags: # Case-insensitive but case preserving merging lotags = [t.lower() for t in other.tags] lstags = [t.lower() for t in self.tags] ot, st = map(frozenset, (lotags, lstags)) for t in st.intersection(ot): sidx = lstags.index(t) oidx = lotags.index(t) self.tags[sidx] = other.tags[oidx] self.tags += [t for t in other.tags if t.lower() in ot-st]

if getattr(other, 'cover_data', False): other_cover = other.cover_data[-1] self_cover = self.cover_data[-1] if self.cover_data else b'' if not self_cover: self_cover = b'' if not other_cover: other_cover = b'' if len(other_cover) > len(self_cover): self.cover_data = other.cover_data if callable(getattr(other, 'custom_field_keys', None)): for x in other.custom_field_keys(): meta = other.get_user_metadata(x, make_copy=True) if meta is not None: self_tags = self.get(x, []) if isinstance(self_tags, string_or_bytes): self_tags = [] self.set_user_metadata(x, meta) # get... did the deepcopy other_tags = other.get(x, []) if meta['datatype'] == 'text' and meta['is_multiple']: # Case-insensitive but case preserving merging lotags = [t.lower() for t in other_tags] try: lstags = [t.lower() for t in self_tags] except TypeError: # Happens if x is not a text, is_multiple field # on self lstags = [] self_tags = [] ot, st = map(frozenset, (lotags, lstags)) for t in st.intersection(ot): sidx = lstags.index(t) oidx = lotags.index(t) self_tags[sidx] = other_tags[oidx] self_tags += [t for t in other_tags if t.lower() in ot-st] setattr(self, x, self_tags)

my_comments = getattr(self, 'comments', '') other_comments = getattr(other, 'comments', '') if not my_comments: my_comments = '' if not other_comments: other_comments = '' if len(other_comments.strip()) > len(my_comments.strip()): self.comments = other_comments

# Copy all the non-none identifiers if callable(getattr(other, 'get_identifiers', None)): d = self.get_identifiers() s = other.get_identifiers() d.update([v for v in iteritems(s) if v[1] is not None]) self.set_identifiers(d) else: # other structure not Metadata. Copy the top-level identifiers for attr in TOP_LEVEL_IDENTIFIERS: copy_not_none(self, other, attr)

other_lang = getattr(other, 'languages', []) if other_lang and other_lang != ['und']: self.languages = list(other_lang) if not getattr(self, 'series', None): self.series_index = Nonedef format_series_index(self, val=None): from calibre.ebooks.metadata import fmt_sidx v = self.series_index if val is None else val try: x = float(v) except Exception: x = 1 return fmt_sidx(x)

def authors_from_string(self, raw): from calibre.ebooks.metadata import string_to_authors self.authors = string_to_authors(raw)

def format_authors(self): from calibre.ebooks.metadata import authors_to_string return authors_to_string(self.authors)

def format_tags(self): return ', '.join([str(t) for t in sorted(self.tags, key=sort_key)])

def format_rating(self, v=None, divide_by=1): if v is None: if self.rating is not None: return str(self.rating/divide_by) return 'None' return str(v/divide_by)

[belgeler] def format_field(self, key, series_with_index=True): ''' Returns the tuple (display_name, formatted_value) ''' name, val, ign, ign = self.format_field_extended(key, series_with_index) return (name, val)

def format_field_extended(self, key, series_with_index=True): from calibre.ebooks.metadata import authors_to_string ''' returns the tuple (display_name, formatted_value, original_value, field_metadata) ''' from calibre.utils.date import format_date

# Handle custom series index if key.startswith('#') and key.endswith('_index'): tkey = key[:-6] # strip the _index cmeta = self.get_user_metadata(tkey, make_copy=False) if cmeta and cmeta['datatype'] == 'series': if self.get(tkey): res = self.get_extra(tkey) return (str(cmeta['name']+'_index'), self.format_series_index(res), res, cmeta) else: return (str(cmeta['name']+'_index'), '', '', cmeta)

if key in self.custom_field_keys(): res = self.get(key, None) # get evaluates all necessary composites cmeta = self.get_user_metadata(key, make_copy=False) name = str(cmeta['name']) if res is None or res == '': # can't check "not res" because of numeric fields return (name, res, None, None) orig_res = res datatype = cmeta['datatype'] if datatype == 'text' and cmeta['is_multiple']: res = cmeta['is_multiple']['list_to_ui'].join(res) elif datatype == 'series' and series_with_index: if self.get_extra(key) is not None: res = res + \ ' [%s]'%self.format_series_index(val=self.get_extra(key)) elif datatype == 'datetime': res = format_date(res, cmeta['display'].get('date_format','dd MMM yyyy')) elif datatype == 'bool': res = _('Yes') if res else _('No') elif datatype == 'rating': res = '%.2g'%(res/2) elif datatype in ['int', 'float']: try: fmt = cmeta['display'].get('number_format', None) res = fmt.format(res) except: pass return (name, str(res), orig_res, cmeta)

# convert top-level ids into their value if key in TOP_LEVEL_IDENTIFIERS: fmeta = field_metadata['identifiers'] name = key res = self.get(key, None) return (name, res, res, fmeta)

# Translate aliases into the standard field name fmkey = field_metadata.search_term_to_field_key(key) if fmkey in field_metadata and field_metadata[fmkey]['kind'] == 'field': res = self.get(key, None) fmeta = field_metadata[fmkey] name = str(fmeta['name']) if res is None or res == '': return (name, res, None, None) orig_res = res name = str(fmeta['name']) datatype = fmeta['datatype'] if key == 'authors': res = authors_to_string(res) elif key == 'series_index': res = self.format_series_index(res) elif datatype == 'text' and fmeta['is_multiple']: if isinstance(res, dict): res = [k + ':' + v for k,v in res.items()] res = fmeta['is_multiple']['list_to_ui'].join(sorted(filter(None, res), key=sort_key)) elif datatype == 'series' and series_with_index: res = res + ' [%s]'%self.format_series_index() elif datatype == 'datetime': res = format_date(res, fmeta['display'].get('date_format','dd MMM yyyy')) elif datatype == 'rating': res = '%.2g'%(res/2) elif key == 'size': res = human_readable(res) return (name, str(res), orig_res, fmeta)

return (None, None, None, None)

def __unicode__representation__(self): ''' A string representation of this object, suitable for printing to console ''' from calibre.ebooks.metadata import authors_to_string from calibre.utils.date import isoformat ans = []

def fmt(x, y): ans.append('%-20s: %s'%(str(x), str(y)))

fmt('Title', self.title) if self.title_sort: fmt('Title sort', self.title_sort) if self.authors: fmt('Author(s)', authors_to_string(self.authors) + ((' [' + self.author_sort + ']') if self.author_sort and self.author_sort != _('Unknown') else '')) if self.publisher: fmt('Publisher', self.publisher) if getattr(self, 'book_producer', False): fmt('Book Producer', self.book_producer) if self.tags: fmt('Tags', ', '.join([str(t) for t in self.tags])) if self.series: fmt('Series', self.series + ' #%s'%self.format_series_index()) if not self.is_null('languages'): fmt('Languages', ', '.join(self.languages)) if self.rating is not None: fmt('Rating', ('%.2g'%(float(self.rating)/2)) if self.rating else '') if self.timestamp is not None: fmt('Timestamp', isoformat(self.timestamp)) if self.pubdate is not None: fmt('Published', isoformat(self.pubdate)) if self.rights is not None: fmt('Rights', str(self.rights)) if self.identifiers: fmt('Identifiers', ', '.join(['%s:%s'%(k, v) for k, v in iteritems(self.identifiers)])) if self.comments: fmt('Comments', self.comments)

for key in self.custom_field_keys(): val = self.get(key, None) if val: (name, val) = self.format_field(key) fmt(name, str(val)) return '\n'.join(ans)

[belgeler] def to_html(self): ''' A HTML representation of this object. ''' from calibre.ebooks.metadata import authors_to_string from calibre.utils.date import isoformat ans = [(_('Title'), str(self.title))] ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))] ans += [(_('Publisher'), str(self.publisher))] ans += [(_('Producer'), str(self.book_producer))] ans += [(_('Comments'), str(self.comments))] ans += [('ISBN', str(self.isbn))] ans += [(_('Tags'), ', '.join([str(t) for t in self.tags]))] if self.series: ans += [(ngettext('Series', 'Series', 1), str(self.series) + ' #%s'%self.format_series_index())] ans += [(_('Languages'), ', '.join(self.languages))] if self.timestamp is not None: ans += [(_('Timestamp'), str(isoformat(self.timestamp, as_utc=False, sep=' ')))] if self.pubdate is not None: ans += [(_('Published'), str(isoformat(self.pubdate, as_utc=False, sep=' ')))] if self.rights is not None: ans += [(_('Rights'), str(self.rights))] for key in self.custom_field_keys(): val = self.get(key, None) if val: (name, val) = self.format_field(key) ans += [(name, val)] for i, x in enumerate(ans): ans[i] = '<tr><td><b>%s</b></td><td>%s</td></tr>'%x return '<table>%s</table>'%'\n'.join(ans)__str__ = __unicode__representation__ def __nonzero__(self): return bool(self.title or self.author or self.comments or self.tags) __bool__ = __nonzero__# }}}

def field_from_string(field, raw, field_metadata): ''' Parse the string raw to return an object that is suitable for calling set() on a Metadata object. ''' dt = field_metadata['datatype'] val = object if dt in {'int', 'float'}: val = int(raw) if dt == 'int' else float(raw) elif dt == 'rating': val = float(raw) * 2 elif dt == 'datetime': from calibre.utils.iso8601 import parse_iso8601 try: val = parse_iso8601(raw, require_aware=True) except Exception: from calibre.utils.date import parse_only_date val = parse_only_date(raw) elif dt == 'bool': if raw.lower() in {'true', 'yes', 'y'}: val = True elif raw.lower() in {'false', 'no', 'n'}: val = False else: raise ValueError('Unknown value for %s: %s'%(field, raw)) elif dt == 'text': ism = field_metadata['is_multiple'] if ism: val = [x.strip() for x in raw.split(ism['ui_to_list'])] if field == 'identifiers': val = {x.partition(':')[0]:x.partition(':')[-1] for x in val} elif field == 'languages': from calibre.utils.localization import canonicalize_lang val = [canonicalize_lang(x) for x in val] val = [x for x in val if x] if val is object: val = raw return val

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.metadata.sources.base

calibre.ebooks.metadata.sources.base öğesinin kaynak kodu

#!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai from __future__ import absolute_import, division, print_function, unicode_literals

__license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import re import threading from functools import total_ordering from calibre import browser, random_user_agent from calibre.customize import Plugin from calibre.ebooks.metadata import check_isbn from calibre.ebooks.metadata.author_mapper import cap_author_token from calibre.utils.localization import canonicalize_lang, get_lang from polyglot.builtins import cmp, iteritems def create_log(ostream=None): from calibre.utils.logging import FileStream, ThreadSafeLog log = ThreadSafeLog(level=ThreadSafeLog.DEBUG) log.outputs = [FileStream(ostream)] return log

# Comparing Metadata objects for relevance {{{ words = ("the", "a", "an", "of", "and") prefix_pat = re.compile(r'^(%s)\s+'%("|".join(words))) trailing_paren_pat = re.compile(r'\(.*\)$') whitespace_pat = re.compile(r'\s+')

def cleanup_title(s): if not s: s = _('Unknown') s = s.strip().lower() s = prefix_pat.sub(' ', s) s = trailing_paren_pat.sub('', s) s = whitespace_pat.sub(' ', s) return s.strip()

[belgeler] @total_ordering class InternalMetadataCompareKeyGen:

''' Generate a sort key for comparison of the relevance of Metadata objects, given a search query. This is used only to compare results from the same metadata source, not across different sources.

The sort key ensures that an ascending order sort is a sort by order of decreasing relevance.

The algorithm is:

* Prefer results that have at least one identifier the same as for the query * Prefer results with a cached cover URL * Prefer results with all available fields filled in * Prefer results with the same language as the current user interface language * Prefer results that are an exact title match to the query * Prefer results with longer comments (greater than 10% longer) * Use the relevance of the result as reported by the metadata source's search engine '''

def __init__(self, mi, source_plugin, title, authors, identifiers): same_identifier = 2 idents = mi.get_identifiers() for k, v in iteritems(identifiers): if idents.get(k) == v: same_identifier = 1 break all_fields = 1 if source_plugin.test_fields(mi) is None else 2 exact_title = 1 if title and \ cleanup_title(title) == cleanup_title(mi.title) else 2 language = 1 if mi.language: mil = canonicalize_lang(mi.language) if mil != 'und' and mil != canonicalize_lang(get_lang()): language = 2 has_cover = 2 if (not source_plugin.cached_cover_url_is_reliable or source_plugin.get_cached_cover_url(mi.identifiers) is None) else 1 self.base = (same_identifier, has_cover, all_fields, language, exact_title) self.comments_len = len((mi.comments or '').strip()) self.extra = getattr(mi, 'source_relevance', 0)

def compare_to_other(self, other): a = cmp(self.base, other.base) if a != 0: return a cx, cy = self.comments_len, other.comments_len if cx and cy: t = (cx + cy) / 20 delta = cy - cx if abs(delta) > t: return -1 if delta < 0 else 1 return cmp(self.extra, other.extra)

def __eq__(self, other): return self.compare_to_other(other) == 0 def __ne__(self, other): return self.compare_to_other(other) != 0 def __lt__(self, other): return self.compare_to_other(other) < 0 def __le__(self, other): return self.compare_to_other(other) <= 0 def __gt__(self, other): return self.compare_to_other(other) > 0 def __ge__(self, other): return self.compare_to_other(other) >= 0# }}}

def get_cached_cover_urls(mi): from calibre.customize.ui import metadata_plugins plugins = list(metadata_plugins(['identify'])) for p in plugins: url = p.get_cached_cover_url(mi.identifiers) if url: yield (p, url)

def dump_caches(): from calibre.customize.ui import metadata_plugins return {p.name:p.dump_caches() for p in metadata_plugins(['identify'])}

def load_caches(dump): from calibre.customize.ui import metadata_plugins plugins = list(metadata_plugins(['identify'])) for p in plugins: cache = dump.get(p.name, None) if cache: p.load_caches(cache)

def fixauthors(authors): if not authors: return authors ans = [] for x in authors: ans.append(' '.join(map(cap_author_token, x.split()))) return ans def fixcase(x): if x: from calibre.utils.titlecase import titlecase x = titlecase(x) return x class Option: __slots__ = ['type', 'default', 'label', 'desc', 'name', 'choices']

def __init__(self, name, type_, default, label, desc, choices=None): ''' :param name: The name of this option. Must be a valid python identifier :param type_: The type of this option, one of ('number', 'string', 'bool', 'choices') :param default: The default value for this option :param label: A short (few words) description of this option :param desc: A longer description of this option :param choices: A dict of possible values, used only if type='choices'. dict is of the form {key:human readable label, ...} ''' self.name, self.type, self.default, self.label, self.desc = (name, type_, default, label, desc) if choices and not isinstance(choices, dict): choices = dict([(x, x) for x in choices]) self.choices = choices

[belgeler] class Source(Plugin):

type = _('Metadata source') author = 'Kovid Goyal'

supported_platforms = ['windows', 'osx', 'linux']

#: Set of capabilities supported by this plugin. #: Useful capabilities are: 'identify', 'cover' capabilities = frozenset()

#: List of metadata fields that can potentially be download by this plugin #: during the identify phase touched_fields = frozenset()

#: Set this to True if your plugin returns HTML formatted comments has_html_comments = False

#: Setting this to True means that the browser object will indicate #: that it supports gzip transfer encoding. This can speedup downloads #: but make sure that the source actually supports gzip transfer encoding #: correctly first supports_gzip_transfer_encoding = False

#: Set this to True to ignore HTTPS certificate errors when connecting #: to this source. ignore_ssl_errors = False

#: Cached cover URLs can sometimes be unreliable (i.e. the download could #: fail or the returned image could be bogus). If that is often the case #: with this source, set to False cached_cover_url_is_reliable = True

#: A list of :class:`Option` objects. They will be used to automatically #: construct the configuration widget for this plugin options = ()

#: A string that is displayed at the top of the config widget for this #: plugin config_help_message = None

#: If True this source can return multiple covers for a given query can_get_multiple_covers = False

#: If set to True covers downloaded by this plugin are automatically trimmed. auto_trim_covers = False

#: If set to True, and this source returns multiple results for a query, #: some of which have ISBNs and some of which do not, the results without #: ISBNs will be ignored prefer_results_with_isbn = True def __init__(self, *args, **kwargs): Plugin.__init__(self, *args, **kwargs) self.running_a_test = False # Set to True when using identify_test() self._isbn_to_identifier_cache = {} self._identifier_to_cover_url_cache = {} self.cache_lock = threading.RLock() self._config_obj = None self._browser = None self.prefs.defaults['ignore_fields'] = [] for opt in self.options: self.prefs.defaults[opt.name] = opt.default

# Configuration {{{

[belgeler] def is_configured(self): ''' Return False if your plugin needs to be configured before it can be used. For example, it might need a username/password/API key. ''' return True
def is_customizable(self): return True

[belgeler] def customization_help(self): return 'This plugin can only be customized using the GUI'

[belgeler] def config_widget(self): from calibre.gui2.metadata.config import ConfigWidget return ConfigWidget(self)

[belgeler] def save_settings(self, config_widget): config_widget.commit()

@property def prefs(self): if self._config_obj is None: from calibre.utils.config import JSONConfig self._config_obj = JSONConfig('metadata_sources/%s.json'%self.name) return self._config_obj # }}}

# Browser {{{

@property def user_agent(self): # Pass in an index to random_user_agent() to test with a particular # user agent return random_user_agent()

@property def browser(self): if self._browser is None: self._browser = browser(user_agent=self.user_agent, verify_ssl_certificates=not self.ignore_ssl_errors) if self.supports_gzip_transfer_encoding: self._browser.set_handle_gzip(True) return self._browser.clone_browser()

# }}}

# Caching {{{ def get_related_isbns(self, id_): with self.cache_lock: for isbn, q in iteritems(self._isbn_to_identifier_cache): if q == id_: yield isbn def cache_isbn_to_identifier(self, isbn, identifier): with self.cache_lock: self._isbn_to_identifier_cache[isbn] = identifier def cached_isbn_to_identifier(self, isbn): with self.cache_lock: return self._isbn_to_identifier_cache.get(isbn, None)

def cache_identifier_to_cover_url(self, id_, url): with self.cache_lock: self._identifier_to_cover_url_cache[id_] = url def cached_identifier_to_cover_url(self, id_): with self.cache_lock: return self._identifier_to_cover_url_cache.get(id_, None)

def dump_caches(self): with self.cache_lock: return {'isbn_to_identifier':self._isbn_to_identifier_cache.copy(), 'identifier_to_cover':self._identifier_to_cover_url_cache.copy()}

def load_caches(self, dump): with self.cache_lock: self._isbn_to_identifier_cache.update(dump['isbn_to_identifier']) self._identifier_to_cover_url_cache.update(dump['identifier_to_cover'])

# }}}

# Utility functions {{{

[belgeler] def get_author_tokens(self, authors, only_first_author=True): ''' Take a list of authors and return a list of tokens useful for an AND search query. This function tries to return tokens in first name middle names last name order, by assuming that if a comma is in the author name, the name is in lastname, other names form. '''

if authors: # Leave ' in there for Irish names remove_pat = re.compile(r'[!@#$%^&*()（）「」{}`~"\s\[\]/]') replace_pat = re.compile(r'[-+.:;,，。；：]') if only_first_author: authors = authors[:1] for au in authors: has_comma = ',' in au au = replace_pat.sub(' ', au) parts = au.split() if has_comma: # au probably in ln, fn form parts = parts[1:] + parts[:1] for tok in parts: tok = remove_pat.sub('', tok).strip() if len(tok) > 2 and tok.lower() not in ('von', 'van', _('Unknown').lower()): yield tok

[belgeler] def get_title_tokens(self, title, strip_joiners=True, strip_subtitle=False): ''' Take a title and return a list of tokens useful for an AND search query. Excludes connectives(optionally) and punctuation. ''' if title: # strip sub-titles if strip_subtitle: subtitle = re.compile(r'([\(\[\{].*?[\)\]\}]|[/:\\].*$)') if len(subtitle.sub('', title)) > 1: title = subtitle.sub('', title)

title_patterns = [(re.compile(pat, re.IGNORECASE), repl) for pat, repl in [ # Remove things like: (2010) (Omnibus) etc. (r'(?i)[({\[](\d{4}|omnibus|anthology|hardcover|audiobook|audio\scd|paperback|turtleback|mass\s*market|edition|ed\.)[\])}]', ''), # Remove any strings that contain the substring edition inside # parentheses (r'(?i)[({\[].*?(edition|ed.).*?[\]})]', ''), # Remove commas used a separators in numbers (r'(\d+),(\d+)', r'\1\2'), # Remove hyphens only if they have whitespace before them (r'(\s-)', ' '), # Replace other special chars with a space (r'''[:,;!@$%^&*(){}.`~"\s\[\]/]《》「」“”''', ' '), ]]

for pat, repl in title_patterns: title = pat.sub(repl, title)

tokens = title.split() for token in tokens: token = token.strip().strip('"').strip("'") if token and (not strip_joiners or token.lower() not in ('a', 'and', 'the', '&')): yield token

[belgeler] def split_jobs(self, jobs, num): 'Split a list of jobs into at most num groups, as evenly as possible' groups = [[] for i in range(num)] jobs = list(jobs) while jobs: for gr in groups: try: job = jobs.pop() except IndexError: break gr.append(job) return [g for g in groups if g]

[belgeler] def test_fields(self, mi): ''' Return the first field from self.touched_fields that is null on the mi object ''' for key in self.touched_fields: if key.startswith('identifier:'): key = key.partition(':')[-1] if not mi.has_identifier(key): return 'identifier: ' + key elif mi.is_null(key): return key

[belgeler] def clean_downloaded_metadata(self, mi): ''' Call this method in your plugin's identify method to normalize metadata before putting the Metadata object into result_queue. You can of course, use a custom algorithm suited to your metadata source. ''' docase = mi.language == 'eng' or mi.is_null('language') if docase and mi.title: mi.title = fixcase(mi.title) mi.authors = fixauthors(mi.authors) if mi.tags and docase: mi.tags = list(map(fixcase, mi.tags)) mi.isbn = check_isbn(mi.isbn)

def download_multiple_covers(self, title, authors, urls, get_best_cover, timeout, result_queue, abort, log, prefs_name='max_covers'): if not urls: log('No images found for, title: %r and authors: %r'%(title, authors)) return import time from threading import Thread if prefs_name: urls = urls[:self.prefs[prefs_name]] if get_best_cover: urls = urls[:1] log('Downloading %d covers'%len(urls)) workers = [Thread(target=self.download_image, args=(u, timeout, log, result_queue)) for u in urls] for w in workers: w.daemon = True w.start() alive = True start_time = time.time() while alive and not abort.is_set() and time.time() - start_time < timeout: alive = False for w in workers: if w.is_alive(): alive = True break abort.wait(0.1)

def download_image(self, url, timeout, log, result_queue): try: ans = self.browser.open_novisit(url, timeout=timeout).read() result_queue.put((self, ans)) log('Downloaded cover from: %s'%url) except Exception: self.log.exception('Failed to download cover from: %r'%url)

# }}}

# Metadata API {{{

[belgeler] def get_book_url(self, identifiers): ''' Return a 3-tuple or None. The 3-tuple is of the form: (identifier_type, identifier_value, URL). The URL is the URL for the book identified by identifiers at this source. identifier_type, identifier_value specify the identifier corresponding to the URL. This URL must be browsable to by a human using a browser. It is meant to provide a clickable link for the user to easily visit the books page at this source. If no URL is found, return None. This method must be quick, and consistent, so only implement it if it is possible to construct the URL from a known scheme given identifiers. ''' return None

[belgeler] def get_book_url_name(self, idtype, idval, url): ''' Return a human readable name from the return value of get_book_url(). ''' return self.name

[belgeler] def get_book_urls(self, identifiers): ''' Override this method if you would like to return multiple URLs for this book. Return a list of 3-tuples. By default this method simply calls :func:`get_book_url`. ''' data = self.get_book_url(identifiers) if data is None: return () return (data,)

[belgeler] def get_cached_cover_url(self, identifiers): ''' Return cached cover URL for the book identified by the identifiers dictionary or None if no such URL exists.

Note that this method must only return validated URLs, i.e. not URLS that could result in a generic cover image or a not found error. ''' return None

[belgeler] def id_from_url(self, url): ''' Parse a URL and return a tuple of the form: (identifier_type, identifier_value). If the URL does not match the pattern for the metadata source, return None. ''' return None

[belgeler] def identify_results_keygen(self, title=None, authors=None, identifiers={}): ''' Return a function that is used to generate a key that can sort Metadata objects by their relevance given a search query (title, authors, identifiers).

These keys are used to sort the results of a call to :meth:`identify`.

For details on the default algorithm see :class:`InternalMetadataCompareKeyGen`. Re-implement this function in your plugin if the default algorithm is not suitable. ''' def keygen(mi): return InternalMetadataCompareKeyGen(mi, self, title, authors, identifiers) return keygen

[belgeler] def identify(self, log, result_queue, abort, title=None, authors=None, identifiers={}, timeout=30): ''' Identify a book by its Title/Author/ISBN/etc.

If identifiers(s) are specified and no match is found and this metadata source does not store all related identifiers (for example, all ISBNs of a book), this method should retry with just the title and author (assuming they were specified).

If this metadata source also provides covers, the URL to the cover should be cached so that a subsequent call to the get covers API with the same ISBN/special identifier does not need to get the cover URL again. Use the caching API for this.

Every Metadata object put into result_queue by this method must have a `source_relevance` attribute that is an integer indicating the order in which the results were returned by the metadata source for this query. This integer will be used by :meth:`compare_identify_results`. If the order is unimportant, set it to zero for every result.

Make sure that any cover/ISBN mapping information is cached before the Metadata object is put into result_queue.

:param log: A log object, use it to output debugging information/errors :param result_queue: A result Queue, results should be put into it. Each result is a Metadata object :param abort: If abort.is_set() returns True, abort further processing and return as soon as possible :param title: The title of the book, can be None :param authors: A list of authors of the book, can be None :param identifiers: A dictionary of other identifiers, most commonly {'isbn':'1234...'} :param timeout: Timeout in seconds, no network request should hang for longer than timeout. :return: None if no errors occurred, otherwise a unicode representation of the error suitable for showing to the user

''' return None

[belgeler] def download_cover(self, log, result_queue, abort, title=None, authors=None, identifiers={}, timeout=30, get_best_cover=False): ''' Download a cover and put it into result_queue. The parameters all have the same meaning as for :meth:`identify`. Put (self, cover_data) into result_queue.

This method should use cached cover URLs for efficiency whenever possible. When cached data is not present, most plugins simply call identify and use its results.

If the parameter get_best_cover is True and this plugin can get multiple covers, it should only get the "best" one. ''' pass

# }}}

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.container

calibre.ebooks.oeb.polish.container öğesinin kaynak kodu

#!/usr/bin/env python # License: GPLv3 Copyright: 2013, Kovid Goyal <kovid at kovidgoyal.net> import errno import hashlib import logging import os import re import shutil import sys import unicodedata import uuid from collections import defaultdict from io import BytesIO from itertools import count from css_parser import getUrls, replaceUrls from calibre import CurrentDir, walk from calibre.constants import iswindows from calibre.customize.ui import plugin_for_input_format, plugin_for_output_format from calibre.ebooks import escape_xpath_attr from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.conversion.plugins.epub_input import ADOBE_OBFUSCATION, IDPF_OBFUSCATION, decrypt_font_data from calibre.ebooks.conversion.preprocess import CSSPreProcessor as cssp from calibre.ebooks.conversion.preprocess import HTMLPreProcessor from calibre.ebooks.metadata.opf3 import CALIBRE_PREFIX, ensure_prefix, items_with_property, read_prefixes from calibre.ebooks.metadata.utils import parse_opf_version from calibre.ebooks.mobi import MobiError from calibre.ebooks.mobi.reader.headers import MetadataHeader from calibre.ebooks.oeb.base import ( DC11_NS, OEB_DOCS, OEB_STYLES, OPF, OPF2_NS, Manifest, itercsslinks, iterlinks, rewrite_links, serialize, urlquote, urlunquote, ) from calibre.ebooks.oeb.parse_utils import NotHTML, parse_html from calibre.ebooks.oeb.polish.errors import DRMError, InvalidBook from calibre.ebooks.oeb.polish.parsing import parse as parse_html_tweak from calibre.ebooks.oeb.polish.utils import OEB_FONTS, CommentFinder, PositionFinder, adjust_mime_for_epub, guess_type, parse_css from calibre.ptempfile import PersistentTemporaryDirectory, PersistentTemporaryFile from calibre.utils.filenames import hardlink_file, nlinks_file, retry_on_fail from calibre.utils.ipc.simple_worker import WorkerError, fork_job from calibre.utils.logging import default_log from calibre.utils.xml_parse import safe_xml_fromstring from calibre.utils.zipfile import ZipFile from polyglot.builtins import iteritems from polyglot.urllib import urlparse exists, join, relpath = os.path.exists, os.path.join, os.path.relpath OPF_NAMESPACES = {'opf':OPF2_NS, 'dc':DC11_NS} null = object() OEB_FONTS # for plugin compat class CSSPreProcessor(cssp):

def __call__(self, data): return self.MS_PAT.sub(self.ms_sub, data)

def clone_dir(src, dest): ' Clone a folder using hard links for the files, dest must already exist ' for x in os.listdir(src): dpath = os.path.join(dest, x) spath = os.path.join(src, x) if os.path.isdir(spath): os.mkdir(dpath) clone_dir(spath, dpath) else: try: hardlink_file(spath, dpath) except: shutil.copy2(spath, dpath)

def clone_container(container, dest_dir): ' Efficiently clone a container using hard links ' dest_dir = os.path.abspath(os.path.realpath(dest_dir)) clone_data = container.clone_data(dest_dir) cls = type(container) if cls is Container: return cls(None, None, container.log, clone_data=clone_data) return cls(None, container.log, clone_data=clone_data)

def name_to_abspath(name, root): return os.path.abspath(join(root, *name.split('/')))

def abspath_to_name(path, root): return relpath(os.path.abspath(path), root).replace(os.sep, '/')

def name_to_href(name, root, base=None, quote=urlquote): fullpath = name_to_abspath(name, root) basepath = root if base is None else os.path.dirname(name_to_abspath(base, root)) path = relpath(fullpath, basepath).replace(os.sep, '/') return quote(path)

def href_to_name(href, root, base=None): base = root if base is None else os.path.dirname(name_to_abspath(base, root)) try: purl = urlparse(href) except ValueError: return None if purl.scheme or not purl.path: return None href = urlunquote(purl.path) if iswindows and ':' in href: # path manipulations on windows fail for paths with : in them, so we # assume all such paths are invalid/absolute paths. return None fullpath = os.path.join(base, *href.split('/')) try: return unicodedata.normalize('NFC', abspath_to_name(fullpath, root)) except ValueError: return None class ContainerBase: # {{{ ''' A base class that implements just the parsing methods. Useful to create virtual containers for testing. '''

#: The mode used to parse HTML and CSS (polishing uses tweak_mode=False and the editor uses tweak_mode=True) tweak_mode = False def __init__(self, log): self.log = log self.parsed_cache = {} self.mime_map = {} self.encoding_map = {} self.html_preprocessor = HTMLPreProcessor() self.css_preprocessor = CSSPreProcessor()

def guess_type(self, name): ' Return the expected mimetype for the specified file name based on its extension. ' return adjust_mime_for_epub(filename=name, opf_version=self.opf_version_parsed)

def decode(self, data, normalize_to_nfc=True): """ Automatically decode ``data`` into a ``unicode`` object.

:param normalize_to_nfc: Normalize returned unicode to the NFC normal form as is required by both the EPUB and AZW3 formats. """ def fix_data(d): return d.replace('\r\n', '\n').replace('\r', '\n') if isinstance(data, str): return fix_data(data) bom_enc = None if data[:4] in {b'\0\0\xfe\xff', b'\xff\xfe\0\0'}: bom_enc = {b'\0\0\xfe\xff':'utf-32-be', b'\xff\xfe\0\0':'utf-32-le'}[data[:4]] data = data[4:] elif data[:2] in {b'\xff\xfe', b'\xfe\xff'}: bom_enc = {b'\xff\xfe':'utf-16-le', b'\xfe\xff':'utf-16-be'}[data[:2]] data = data[2:] elif data[:3] == b'\xef\xbb\xbf': bom_enc = 'utf-8' data = data[3:] if bom_enc is not None: try: self.used_encoding = bom_enc return fix_data(data.decode(bom_enc)) except UnicodeDecodeError: pass try: self.used_encoding = 'utf-8' return fix_data(data.decode('utf-8')) except UnicodeDecodeError: pass data, self.used_encoding = xml_to_unicode(data) if normalize_to_nfc: data = unicodedata.normalize('NFC', data) return fix_data(data)

def parse_xml(self, data): data, self.used_encoding = xml_to_unicode( data, strip_encoding_pats=True, assume_utf8=True, resolve_entities=True) data = unicodedata.normalize('NFC', data) return safe_xml_fromstring(data)

def parse_xhtml(self, data, fname='<string>', force_html5_parse=False): if self.tweak_mode: return parse_html_tweak(data, log=self.log, decoder=self.decode, force_html5_parse=force_html5_parse) else: try: return parse_html( data, log=self.log, decoder=self.decode, preprocessor=self.html_preprocessor, filename=fname, non_html_file_tags={'ncx'}) except NotHTML: return self.parse_xml(data)

def parse_css(self, data, fname='<string>', is_declaration=False): return parse_css(data, fname=fname, is_declaration=is_declaration, decode=self.decode, log_level=logging.WARNING, css_preprocessor=(None if self.tweak_mode else self.css_preprocessor)) # }}}

[belgeler] class Container(ContainerBase): # {{{

''' A container represents an open e-book as a folder full of files and an OPF file. There are two important concepts:

* The root folder. This is the base of the e-book. All the e-books files are inside this folder or in its sub-folders.

* Names: These are paths to the books' files relative to the root folder. They always contain POSIX separators and are unquoted. They can be thought of as canonical identifiers for files in the book. Most methods on the container object work with names. Names are always in the NFC Unicode normal form.

* Clones: the container object supports efficient on-disk cloning, which is used to implement checkpoints in the e-book editor. In order to make this work, you should never access files on the filesystem directly. Instead, use :meth:`raw_data` or :meth:`open` to read/write to component files in the book.

When converting between hrefs and names use the methods provided by this class, they assume all hrefs are quoted. '''

#: The type of book (epub for EPUB files and azw3 for AZW3 files) book_type = 'oeb' #: If this container represents an unzipped book (a directory) is_dir = False

SUPPORTS_TITLEPAGES = True SUPPORTS_FILENAMES = True

@property def book_type_for_display(self): return self.book_type.upper()

def __init__(self, rootpath, opfpath, log, clone_data=None): ContainerBase.__init__(self, log) self.root = clone_data['root'] if clone_data is not None else os.path.abspath(rootpath)

self.name_path_map = {} self.dirtied = set() self.pretty_print = set() self.cloned = False self.cache_names = ('parsed_cache', 'mime_map', 'name_path_map', 'encoding_map', 'dirtied', 'pretty_print') self.href_to_name_cache = {}

if clone_data is not None: self.cloned = True for x in ('name_path_map', 'opf_name', 'mime_map', 'pretty_print', 'encoding_map', 'tweak_mode'): setattr(self, x, clone_data[x]) self.opf_dir = os.path.dirname(self.name_path_map[self.opf_name]) return

# Map of relative paths with '/' separators from root of unzipped ePub # to absolute paths on filesystem with os-specific separators opfpath = os.path.abspath(os.path.realpath(opfpath)) all_opf_files = [] for dirpath, _dirnames, filenames in os.walk(self.root): for f in filenames: path = join(dirpath, f) name = self.abspath_to_name(path) self.name_path_map[name] = path self.mime_map[name] = guess_type(path) # Special case if we have stumbled onto the opf if path == opfpath: self.opf_name = name self.opf_dir = os.path.dirname(path) self.mime_map[name] = guess_type('a.opf') if path.lower().endswith('.opf'): all_opf_files.append((name, os.path.dirname(path)))

if not hasattr(self, 'opf_name') and all_opf_files: self.opf_name, self.opf_dir = all_opf_files[0] self.mime_map[self.opf_name] = guess_type('a.opf')

if not hasattr(self, 'opf_name'): raise InvalidBook('Could not locate opf file: %r'%opfpath)

# Update mime map with data from the OPF self.refresh_mime_map()

def refresh_mime_map(self): for item in self.opf_xpath('//opf:manifest/opf:item[@href and @media-type]'): href = item.get('href') try: name = self.href_to_name(href, self.opf_name) except ValueError: continue # special filenames such as CON on windows cause relpath to fail mt = item.get('media-type') if name in self.mime_map and name != self.opf_name and mt: # some epubs include the opf in the manifest with an incorrect mime type self.mime_map[name] = mt def data_for_clone(self, dest_dir=None): dest_dir = dest_dir or self.root return { 'root': dest_dir, 'opf_name': self.opf_name, 'mime_map': self.mime_map.copy(), 'pretty_print': set(self.pretty_print), 'encoding_map': self.encoding_map.copy(), 'tweak_mode': self.tweak_mode, 'name_path_map': { name:os.path.join(dest_dir, os.path.relpath(path, self.root)) for name, path in iteritems(self.name_path_map)} }

def clone_data(self, dest_dir): Container.commit(self, keep_parsed=False) self.cloned = True clone_dir(self.root, dest_dir) return self.data_for_clone(dest_dir)

[belgeler] def add_name_to_manifest(self, name, process_manifest_item=None): ' Add an entry to the manifest for a file with the specified name. Returns the manifest id. ' all_ids = {x.get('id') for x in self.opf_xpath('//*[@id]')} c = 0 item_id = 'id' while item_id in all_ids: c += 1 item_id = 'id' + '%d'%c manifest = self.opf_xpath('//opf:manifest')[0] href = self.name_to_href(name, self.opf_name) item = manifest.makeelement(OPF('item'), id=item_id, href=href) item.set('media-type', self.mime_map[name]) self.insert_into_xml(manifest, item) if process_manifest_item is not None: process_manifest_item(item) self.dirty(self.opf_name) return item_id

[belgeler] def manifest_has_name(self, name): ''' Return True if the manifest has an entry corresponding to name ''' all_names = {self.href_to_name(x.get('href'), self.opf_name) for x in self.opf_xpath('//opf:manifest/opf:item[@href]')} return name in all_names

[belgeler] def make_name_unique(self, name): ''' Ensure that `name` does not already exist in this book. If it does, return a modified version that does not exist. ''' counter = count() while self.has_name_case_insensitive(name) or self.manifest_has_name(name): c = next(counter) + 1 base, ext = name.rpartition('.')[::2] if c > 1: base = base.rpartition('-')[0] name = '%s-%d.%s' % (base, c, ext) return name

[belgeler] def add_file(self, name, data, media_type=None, spine_index=None, modify_name_if_needed=False, process_manifest_item=None): ''' Add a file to this container. Entries for the file are automatically created in the OPF manifest and spine (if the file is a text document) ''' if '..' in name: raise ValueError('Names are not allowed to have .. in them') href = self.name_to_href(name, self.opf_name) if self.has_name_case_insensitive(name) or self.manifest_has_name(name): if not modify_name_if_needed: raise ValueError(('A file with the name %s already exists' % name) if self.has_name_case_insensitive(name) else ('An item with the href %s already exists in the manifest' % href)) name = self.make_name_unique(name) href = self.name_to_href(name, self.opf_name) path = self.name_to_abspath(name) base = os.path.dirname(path) if not os.path.exists(base): os.makedirs(base) with open(path, 'wb') as f: if hasattr(data, 'read'): shutil.copyfileobj(data, f) else: f.write(data) mt = media_type or self.guess_type(name) self.name_path_map[name] = path self.mime_map[name] = mt if self.ok_to_be_unmanifested(name): return name item_id = self.add_name_to_manifest(name, process_manifest_item=process_manifest_item) if mt in OEB_DOCS: manifest = self.opf_xpath('//opf:manifest')[0] spine = self.opf_xpath('//opf:spine')[0] si = manifest.makeelement(OPF('itemref'), idref=item_id) self.insert_into_xml(spine, si, index=spine_index) return name[belgeler] def rename(self, current_name, new_name): ''' Renames a file from current_name to new_name. It automatically rebases all links inside the file if the folder the file is in changes. Note however, that links are not updated in the other files that could reference this file. This is for performance, such updates should be done once, in bulk. ''' if current_name in self.names_that_must_not_be_changed: raise ValueError('Renaming of %s is not allowed' % current_name) if self.exists(new_name) and (new_name == current_name or new_name.lower() != current_name.lower()): # The destination exists and does not differ from the current name only by case raise ValueError(f'Cannot rename {current_name} to {new_name} as {new_name} already exists') new_path = self.name_to_abspath(new_name) base = os.path.dirname(new_path) if os.path.isfile(base): raise ValueError(f'Cannot rename {current_name} to {new_name} as {base} is a file') if not os.path.exists(base): os.makedirs(base) old_path = parent_dir = self.name_to_abspath(current_name) self.commit_item(current_name) os.rename(old_path, new_path) # Remove empty directories while parent_dir: parent_dir = os.path.dirname(parent_dir) try: os.rmdir(parent_dir) except OSError: break for x in ('mime_map', 'encoding_map'): x = getattr(self, x) if current_name in x: x[new_name] = x[current_name] self.name_path_map[new_name] = new_path for x in self.cache_names: x = getattr(self, x) try: x.pop(current_name, None) except TypeError: x.discard(current_name) if current_name == self.opf_name: self.opf_name = new_name if os.path.dirname(old_path) != os.path.dirname(new_path): from calibre.ebooks.oeb.polish.replace import LinkRebaser repl = LinkRebaser(self, current_name, new_name) self.replace_links(new_name, repl) self.dirty(new_name)

[belgeler] def replace_links(self, name, replace_func): ''' Replace all links in name using replace_func, which must be a callable that accepts a URL and returns the replaced URL. It must also have a 'replaced' attribute that is set to True if any actual replacement is done. Convenient ways of creating such callables are using the :class:`LinkReplacer` and :class:`LinkRebaser` classes. ''' media_type = self.mime_map.get(name, guess_type(name)) if name == self.opf_name: replace_func.file_type = 'opf' for elem in self.opf_xpath('//*[@href]'): elem.set('href', replace_func(elem.get('href'))) elif media_type.lower() in OEB_DOCS: replace_func.file_type = 'text' rewrite_links(self.parsed(name), replace_func) elif media_type.lower() in OEB_STYLES: replace_func.file_type = 'style' replaceUrls(self.parsed(name), replace_func) elif media_type.lower() == guess_type('toc.ncx'): replace_func.file_type = 'ncx' for elem in self.parsed(name).xpath('//*[@src]'): elem.set('src', replace_func(elem.get('src')))

if replace_func.replaced: self.dirty(name) return replace_func.replaced

[belgeler] def iterlinks(self, name, get_line_numbers=True): ''' Iterate over all links in name. If get_line_numbers is True the yields results of the form (link, line_number, offset). Where line_number is the line_number at which the link occurs and offset is the number of characters from the start of the line. Note that offset could actually encompass several lines if not zero. ''' media_type = self.mime_map.get(name, guess_type(name)) if name == self.opf_name: for elem in self.opf_xpath('//*[@href]'): yield (elem.get('href'), elem.sourceline, 0) if get_line_numbers else elem.get('href') elif media_type.lower() in OEB_DOCS: for el, attr, link, pos in iterlinks(self.parsed(name)): yield (link, el.sourceline, pos) if get_line_numbers else link elif media_type.lower() in OEB_STYLES: if get_line_numbers: with self.open(name, 'rb') as f: raw = self.decode(f.read()).replace('\r\n', '\n').replace('\r', '\n') position = PositionFinder(raw) is_in_comment = CommentFinder(raw) for link, offset in itercsslinks(raw): if not is_in_comment(offset): lnum, col = position(offset) yield link, lnum, col else: for link in getUrls(self.parsed(name)): yield link elif media_type.lower() == guess_type('toc.ncx'): for elem in self.parsed(name).xpath('//*[@src]'): yield (elem.get('src'), elem.sourceline, 0) if get_line_numbers else elem.get('src')

[belgeler] def abspath_to_name(self, fullpath, root=None): ''' Convert an absolute path to a canonical name relative to :attr:`root`

:param root: The base folder. By default the root for this container object is used. ''' # OS X silently changes all file names to NFD form. The EPUB # spec requires all text including filenames to be in NFC form. # The proper fix is to implement a VFS that maps between # canonical names and their file system representation, however, # I dont have the time for that now. Note that the container # ensures that all text files are normalized to NFC when # decoding them anyway, so there should be no mismatch between # names in the text and NFC canonical file names. return unicodedata.normalize('NFC', abspath_to_name(fullpath, root or self.root))

[belgeler] def name_to_abspath(self, name): ' Convert a canonical name to an absolute OS dependent path ' return name_to_abspath(name, self.root)

[belgeler] def exists(self, name): ''' True iff a file/folder corresponding to the canonical name exists. Note that this function suffers from the limitations of the underlying OS filesystem, in particular case (in)sensitivity. So on a case insensitive filesystem this will return True even if the case of name is different from the case of the underlying filesystem file. See also :meth:`has_name`''' return os.path.exists(self.name_to_abspath(name))

[belgeler] def href_to_name(self, href, base=None): ''' Convert an href (relative to base) to a name. base must be a name or None, in which case self.root is used. ''' key = href, base ans = self.href_to_name_cache.get(key, null) if ans is null: ans = self.href_to_name_cache[key] = href_to_name(href, self.root, base=base) return ans

[belgeler] def name_to_href(self, name, base=None): '''Convert a name to a href relative to base, which must be a name or None in which case self.root is used as the base''' return name_to_href(name, self.root, base=base)

[belgeler] def opf_xpath(self, expr): ' Convenience method to evaluate an XPath expression on the OPF file, has the opf: and dc: namespace prefixes pre-defined. ' return self.opf.xpath(expr, namespaces=OPF_NAMESPACES)

[belgeler] def has_name(self, name): ''' Return True iff a file with the same canonical name as that specified exists. Unlike :meth:`exists` this method is always case-sensitive. ''' return name and name in self.name_path_map
def has_name_and_is_not_empty(self, name): if not self.has_name(name): return False try: return os.path.getsize(self.name_path_map[name]) > 0 except OSError: return False def has_name_case_insensitive(self, name): if not name: return False name = name.lower() for q in self.name_path_map: if q.lower() == name: return True return False

[belgeler] def relpath(self, path, base=None): '''Convert an absolute path (with os separators) to a path relative to base (defaults to self.root). The relative path is *not* a name. Use :meth:`abspath_to_name` for that.''' return relpath(path, base or self.root)

def ok_to_be_unmanifested(self, name): return name in self.names_that_need_not_be_manifested

@property def names_that_need_not_be_manifested(self): ' Set of names that are allowed to be missing from the manifest. Depends on the e-book file format. ' return {self.opf_name}

@property def names_that_must_not_be_removed(self): ' Set of names that must never be deleted from the container. Depends on the e-book file format. ' return {self.opf_name}

@property def names_that_must_not_be_changed(self): ' Set of names that must never be renamed. Depends on the e-book file format. ' return set()

def parse(self, path, mime): with open(path, 'rb') as src: data = src.read() if mime in OEB_DOCS: data = self.parse_xhtml(data, self.relpath(path)) elif mime[-4:] in {'+xml', '/xml'}: data = self.parse_xml(data) elif mime in OEB_STYLES: data = self.parse_css(data, self.relpath(path)) return data

[belgeler] def raw_data(self, name, decode=True, normalize_to_nfc=True): ''' Return the raw data corresponding to the file specified by name

:param decode: If True and the file has a text based MIME type, decode it and return a unicode object instead of raw bytes. :param normalize_to_nfc: If True the returned unicode object is normalized to the NFC normal form as is required for the EPUB and AZW3 file formats. ''' with self.open(name) as nf: ans = nf.read() mime = self.mime_map.get(name, guess_type(name)) if decode and (mime in OEB_STYLES or mime in OEB_DOCS or mime == 'text/plain' or mime[-4:] in {'+xml', '/xml'}): ans = self.decode(ans, normalize_to_nfc=normalize_to_nfc) return ans

[belgeler] def parsed(self, name): ''' Return a parsed representation of the file specified by name. For HTML and XML files an lxml tree is returned. For CSS files a css_parser stylesheet is returned. Note that parsed objects are cached for performance. If you make any changes to the parsed object, you must call :meth:`dirty` so that the container knows to update the cache. See also :meth:`replace`.''' ans = self.parsed_cache.get(name, None) if ans is None: self.used_encoding = None mime = self.mime_map.get(name, guess_type(name)) ans = self.parse(self.name_path_map[name], mime) self.parsed_cache[name] = ans self.encoding_map[name] = self.used_encoding return ans

[belgeler] def replace(self, name, obj): ''' Replace the parsed object corresponding to name with obj, which must be a similar object, i.e. an lxml tree for HTML/XML or a css_parser stylesheet for a CSS file. ''' self.parsed_cache[name] = obj self.dirty(name)

@property def opf(self): ' The parsed OPF file ' return self.parsed(self.opf_name)

@property def mi(self): ''' The metadata of this book as a Metadata object. Note that this object is constructed on the fly every time this property is requested, so use it sparingly. ''' from calibre.ebooks.metadata.opf2 import OPF as O mi = self.serialize_item(self.opf_name) return O(BytesIO(mi), basedir=self.opf_dir, unquote_urls=False, populate_spine=False).to_book_metadata()

@property def opf_version(self): ' The version set on the OPF\'s <package> element ' try: return self.opf_xpath('//opf:package/@version')[0] except IndexError: return ''

@property def opf_version_parsed(self): ' The version set on the OPF\'s <package> element as a tuple of integers ' return parse_opf_version(self.opf_version)

@property def manifest_id_map(self): ' Mapping of manifest id to canonical names ' return {item.get('id'):self.href_to_name(item.get('href'), self.opf_name) for item in self.opf_xpath('//opf:manifest/opf:item[@href and @id]')}

@property def manifest_type_map(self): ' Mapping of manifest media-type to list of canonical names of that media-type ' ans = defaultdict(list) for item in self.opf_xpath('//opf:manifest/opf:item[@href and @media-type]'): ans[item.get('media-type').lower()].append(self.href_to_name( item.get('href'), self.opf_name)) return {mt:tuple(v) for mt, v in iteritems(ans)}

[belgeler] def manifest_items_with_property(self, property_name): ' All manifest items that have the specified property ' prefixes = read_prefixes(self.opf) for item in items_with_property(self.opf, property_name, prefixes): href = item.get('href') if href: yield self.href_to_name(item.get('href'), self.opf_name)

[belgeler] def manifest_items_of_type(self, predicate): ''' The names of all manifest items whose media-type matches predicate. `predicate` can be a set, a list, a string or a function taking a single argument, which will be called with the media-type. ''' if isinstance(predicate, str): predicate = predicate.__eq__ elif hasattr(predicate, '__contains__'): predicate = predicate.__contains__ for mt, names in iteritems(self.manifest_type_map): if predicate(mt): yield from names

[belgeler] def apply_unique_properties(self, name, *properties): ''' Ensure that the specified properties are set on only the manifest item identified by name. You can pass None as the name to remove the property from all items. ''' properties = frozenset(properties) removed_names, added_names = [], [] for p in properties: if p.startswith('calibre:'): ensure_prefix(self.opf, None, 'calibre', CALIBRE_PREFIX) break for item in self.opf_xpath('//opf:manifest/opf:item'): iname = self.href_to_name(item.get('href'), self.opf_name) props = (item.get('properties') or '').split() lprops = {p.lower() for p in props} for prop in properties: if prop.lower() in lprops: if name != iname: removed_names.append(iname) props = [p for p in props if p.lower() != prop] if props: item.set('properties', ' '.join(props)) else: del item.attrib['properties'] else: if name == iname: added_names.append(iname) props.append(prop) item.set('properties', ' '.join(props)) self.dirty(self.opf_name) return removed_names, added_names

[belgeler] def add_properties(self, name, *properties): ''' Add the specified properties to the manifest item identified by name. ''' properties = frozenset(properties) if not properties: return True for p in properties: if p.startswith('calibre:'): ensure_prefix(self.opf, None, 'calibre', CALIBRE_PREFIX) break for item in self.opf_xpath('//opf:manifest/opf:item'): iname = self.href_to_name(item.get('href'), self.opf_name) if name == iname: props = frozenset((item.get('properties') or '').split()) | properties item.set('properties', ' '.join(props)) return True return False

@property def guide_type_map(self): ' Mapping of guide type to canonical name ' return {item.get('type', ''):self.href_to_name(item.get('href'), self.opf_name) for item in self.opf_xpath('//opf:guide/opf:reference[@href and @type]')}

@property def spine_iter(self): ''' An iterator that yields item, name is_linear for every item in the books' spine. item is the lxml element, name is the canonical file name and is_linear is True if the item is linear. See also: :attr:`spine_names` and :attr:`spine_items`. ''' manifest_id_map = self.manifest_id_map non_linear = [] for item in self.opf_xpath('//opf:spine/opf:itemref[@idref]'): idref = item.get('idref') name = manifest_id_map.get(idref, None) path = self.name_path_map.get(name, None) if path: if item.get('linear', 'yes') == 'yes': yield item, name, True else: non_linear.append((item, name)) for item, name in non_linear: yield item, name, False def index_in_spine(self, name): manifest_id_map = self.manifest_id_map for i, item in enumerate(self.opf_xpath('//opf:spine/opf:itemref[@idref]')): idref = item.get('idref') q = manifest_id_map.get(idref, None) if q == name: return i

@property def spine_names(self): ''' An iterator yielding name and is_linear for every item in the books' spine. See also: :attr:`spine_iter` and :attr:`spine_items`. ''' for item, name, linear in self.spine_iter: yield name, linear

@property def spine_items(self): ''' An iterator yielding the path for every item in the books' spine. See also: :attr:`spine_iter` and :attr:`spine_items`. ''' for name, linear in self.spine_names: yield self.name_path_map[name]

[belgeler] def remove_from_spine(self, spine_items, remove_if_no_longer_in_spine=True): ''' Remove the specified items (by canonical name) from the spine. If ``remove_if_no_longer_in_spine`` is True, the items are also deleted from the book, not just from the spine. ''' nixed = set() for (name, remove), (item, xname, linear) in zip(spine_items, self.spine_iter): if remove and name == xname: self.remove_from_xml(item) nixed.add(name) if remove_if_no_longer_in_spine: # Remove from the book if no longer in spine nixed -= {name for name, linear in self.spine_names} for name in nixed: self.remove_item(name)

[belgeler] def set_spine(self, spine_items): ''' Set the spine to be spine_items where spine_items is an iterable of the form (name, linear). Will raise an error if one of the names is not present in the manifest. ''' imap = self.manifest_id_map imap = {name:item_id for item_id, name in iteritems(imap)} items = [item for item, name, linear in self.spine_iter] tail, last_tail = (items[0].tail, items[-1].tail) if items else ('\n ', '\n ') for i in items: self.remove_from_xml(i) spine = self.opf_xpath('//opf:spine')[0] spine.text = tail for name, linear in spine_items: i = spine.makeelement('{%s}itemref' % OPF_NAMESPACES['opf'], nsmap={'opf':OPF_NAMESPACES['opf']}) i.tail = tail i.set('idref', imap[name]) spine.append(i) if not linear: i.set('linear', 'no') if len(spine) > 0: spine[-1].tail = last_tail self.dirty(self.opf_name)

[belgeler] def remove_item(self, name, remove_from_guide=True): ''' Remove the item identified by name from this container. This removes all references to the item in the OPF manifest, guide and spine as well as from any internal caches. ''' removed = set() for elem in self.opf_xpath('//opf:manifest/opf:item[@href]'): if self.href_to_name(elem.get('href'), self.opf_name) == name: id_ = elem.get('id', None) if id_ is not None: removed.add(id_) self.remove_from_xml(elem) self.dirty(self.opf_name) if removed: for spine in self.opf_xpath('//opf:spine'): tocref = spine.attrib.get('toc', None) if tocref and tocref in removed: spine.attrib.pop('toc', None) self.dirty(self.opf_name)

for item in self.opf_xpath('//opf:spine/opf:itemref[@idref]'): idref = item.get('idref') if idref in removed: self.remove_from_xml(item) self.dirty(self.opf_name)

for meta in self.opf_xpath('//opf:meta[@name="cover" and @content]'): if meta.get('content') in removed: self.remove_from_xml(meta) self.dirty(self.opf_name)

if remove_from_guide: for item in self.opf_xpath('//opf:guide/opf:reference[@href]'): if self.href_to_name(item.get('href'), self.opf_name) == name: self.remove_from_xml(item) self.dirty(self.opf_name)

path = self.name_path_map.pop(name, None) if path and os.path.exists(path): os.remove(path) self.mime_map.pop(name, None) self.parsed_cache.pop(name, None) self.dirtied.discard(name)

[belgeler] def dirty(self, name): ''' Mark the parsed object corresponding to name as dirty. See also: :meth:`parsed`. ''' self.dirtied.add(name)

[belgeler] def remove_from_xml(self, item): 'Removes item from parent, fixing indentation (works only with self closing items)' parent = item.getparent() idx = parent.index(item) if idx == 0: # We are removing the first item - only care about adjusting # the tail if this was the only child if len(parent) == 1: parent.text = item.tail else: # Make sure the preceding item has this tail parent[idx-1].tail = item.tail parent.remove(item) return item

[belgeler] def insert_into_xml(self, parent, item, index=None): '''Insert item into parent (or append if index is None), fixing indentation. Only works with self closing items.''' if index is None: parent.append(item) else: parent.insert(index, item) idx = parent.index(item) if idx == 0: item.tail = parent.text # If this is the only child of this parent element, we need a # little extra work as we have gone from a self-closing <foo /> # element to <foo><item /></foo> if len(parent) == 1: sibling = parent.getprevious() if sibling is None: # Give up! return parent.text = sibling.text item.tail = sibling.tail else: item.tail = parent[idx-1].tail if idx == len(parent)-1: parent[idx-1].tail = parent.text

[belgeler] def opf_get_or_create(self, name): ''' Convenience method to either return the first XML element with the specified name or create it under the opf:package element and then return it, if it does not already exist. ''' ans = self.opf_xpath('//opf:'+name) if ans: return ans[0] self.dirty(self.opf_name) package = self.opf_xpath('//opf:package')[0] item = package.makeelement(OPF(name)) item.tail = '\n' package.append(item) return item

[belgeler] def generate_item(self, name, id_prefix=None, media_type=None, unique_href=True): '''Add an item to the manifest with href derived from the given name. Ensures uniqueness of href and id automatically. Returns generated item.''' id_prefix = id_prefix or 'id' media_type = media_type or self.guess_type(name) if unique_href: name = self.make_name_unique(name) href = self.name_to_href(name, self.opf_name) base, ext = href.rpartition('.')[0::2] all_ids = {x.get('id') for x in self.opf_xpath('//*[@id]')} c = 0 item_id = id_prefix while item_id in all_ids: c += 1 item_id = id_prefix + '%d'%c manifest = self.opf_xpath('//opf:manifest')[0] item = manifest.makeelement(OPF('item'), id=item_id, href=href) item.set('media-type', media_type) self.insert_into_xml(manifest, item) self.dirty(self.opf_name) name = self.href_to_name(href, self.opf_name) self.name_path_map[name] = path = self.name_to_abspath(name) self.mime_map[name] = media_type # Ensure that the file corresponding to the newly created item exists # otherwise cloned containers will fail when they try to get the number # of links to the file base = os.path.dirname(path) if not os.path.exists(base): os.makedirs(base) open(path, 'wb').close() return itemdef format_opf(self): try: mdata = self.opf_xpath('//opf:metadata')[0] except IndexError: pass else: mdata.text = '\n ' remove = set() for child in mdata: child.tail = '\n ' try: if (child.get('name', '').startswith('calibre:' ) and child.get('content', '').strip() in {'{}', ''}): remove.add(child) except AttributeError: continue # Happens for XML comments for child in remove: mdata.remove(child) if len(mdata) > 0: mdata[-1].tail = '\n ' # Ensure name comes before content, needed for Nooks for meta in self.opf_xpath('//opf:meta[@name="cover"]'): if 'content' in meta.attrib: meta.set('content', meta.attrib.pop('content'))

[belgeler] def serialize_item(self, name): ''' Convert a parsed object (identified by canonical name) into a bytestring. See :meth:`parsed`. ''' data = root = self.parsed(name) if name == self.opf_name: self.format_opf() data = serialize(data, self.mime_map[name], pretty_print=name in self.pretty_print) if name == self.opf_name and root.nsmap.get(None) == OPF2_NS: # Needed as I can't get lxml to output opf:role and # not output <opf:metadata> as well data = re.sub(br'(<[/]{0,1})opf:', br'\1', data) return data

[belgeler] def commit_item(self, name, keep_parsed=False): ''' Commit a parsed object to disk (it is serialized and written to the underlying file). If ``keep_parsed`` is True the parsed representation is retained in the cache. See also: :meth:`parsed` ''' if name not in self.parsed_cache: return data = self.serialize_item(name) self.dirtied.discard(name) if not keep_parsed: self.parsed_cache.pop(name) dest = self.name_path_map[name] if self.cloned and nlinks_file(dest) > 1: # Decouple this file from its links os.unlink(dest) with open(dest, 'wb') as f: f.write(data)

[belgeler] def filesize(self, name): ''' Return the size in bytes of the file represented by the specified canonical name. Automatically handles dirtied parsed objects. See also: :meth:`parsed` ''' if name in self.dirtied: self.commit_item(name, keep_parsed=True) path = self.name_to_abspath(name) return os.path.getsize(path)

[belgeler] def get_file_path_for_processing(self, name, allow_modification=True): ''' Similar to open() except that it returns a file path, instead of an open file object. ''' if name in self.dirtied: self.commit_item(name) self.parsed_cache.pop(name, False) path = self.name_to_abspath(name) base = os.path.dirname(path) if not os.path.exists(base): os.makedirs(base) else: if self.cloned and allow_modification and os.path.exists(path) and nlinks_file(path) > 1: # Decouple this file from its links temp = path + 'xxx' shutil.copyfile(path, temp) if iswindows: retry_on_fail(os.unlink, path) else: os.unlink(path) os.rename(temp, path) return path

[belgeler] def open(self, name, mode='rb'): ''' Open the file pointed to by name for direct read/write. Note that this will commit the file if it is dirtied and remove it from the parse cache. You must finish with this file before accessing the parsed version of it again, or bad things will happen. ''' return open(self.get_file_path_for_processing(name, mode not in {'r', 'rb'}), mode)

[belgeler] def commit(self, outpath=None, keep_parsed=False): ''' Commit all dirtied parsed objects to the filesystem and write out the e-book file at outpath.

:param output: The path to write the saved e-book file to. If None, the path of the original book file is used. :param keep_parsed: If True the parsed representations of committed items are kept in the cache. ''' for name in tuple(self.dirtied): self.commit_item(name, keep_parsed=keep_parsed)

def compare_to(self, other): if set(self.name_path_map) != set(other.name_path_map): return 'Set of files is not the same' mismatches = [] for name, path in iteritems(self.name_path_map): opath = other.name_path_map[name] with open(path, 'rb') as f1, open(opath, 'rb') as f2: if f1.read() != f2.read(): mismatches.append('The file %s is not the same'%name) return '\n'.join(mismatches)# }}}

# EPUB {{{ class InvalidEpub(InvalidBook): pass class ObfuscationKeyMissing(InvalidEpub): pass

OCF_NS = 'urn:oasis:names:tc:opendocument:xmlns:container' VCS_IGNORE_FILES = frozenset('.gitignore .hgignore .agignore .bzrignore'.split()) VCS_DIRS = frozenset(('.git', '.hg', '.svn', '.bzr'))

def walk_dir(basedir): for dirpath, dirnames, filenames in os.walk(basedir): for vcsdir in VCS_DIRS: try: dirnames.remove(vcsdir) except Exception: pass is_root = os.path.abspath(os.path.normcase(dirpath)) == os.path.abspath(os.path.normcase(basedir)) yield is_root, dirpath, None for fname in filenames: if fname not in VCS_IGNORE_FILES: yield is_root, dirpath, fname class EpubContainer(Container):

book_type = 'epub'

@property def book_type_for_display(self): ans = self.book_type.upper() try: v = self.opf_version_parsed except Exception: pass else: try: if v.major == 2: ans += ' 2' else: if not v.minor: ans += f' {v.major}' else: ans += f' {v.major}.{v.minor}' except Exception: pass return ans

META_INF = { 'container.xml': True, 'manifest.xml': False, 'encryption.xml': False, 'metadata.xml': False, 'signatures.xml': False, 'rights.xml': False, }

def __init__(self, pathtoepub, log, clone_data=None, tdir=None): if clone_data is not None: super().__init__(None, None, log, clone_data=clone_data) for x in ('pathtoepub', 'obfuscated_fonts', 'is_dir'): setattr(self, x, clone_data[x]) return self.pathtoepub = pathtoepub if tdir is None: tdir = PersistentTemporaryDirectory('_epub_container') tdir = os.path.abspath(os.path.realpath(tdir)) self.root = tdir self.is_dir = os.path.isdir(pathtoepub) if self.is_dir: for is_root, dirpath, fname in walk_dir(self.pathtoepub): if is_root: base = tdir else: base = os.path.join(tdir, os.path.relpath(dirpath, self.pathtoepub)) if fname is None: os.mkdir(base) if fname is not None: shutil.copy(os.path.join(dirpath, fname), os.path.join(base, fname)) else: with open(self.pathtoepub, 'rb') as stream: try: zf = ZipFile(stream) zf.extractall(tdir) except: log.exception('EPUB appears to be invalid ZIP file, trying a' ' more forgiving ZIP parser') from calibre.utils.localunzip import extractall stream.seek(0) extractall(stream, path=tdir) try: os.remove(join(tdir, 'mimetype')) except OSError: pass # Ensure all filenames are in NFC normalized form # has no effect on HFS+ filesystems as they always store filenames # in NFD form for filename in walk(self.root): n = unicodedata.normalize('NFC', filename) if n != filename: s = filename + 'suff1x' os.rename(filename, s) os.rename(s, n)

container_path = join(self.root, 'META-INF', 'container.xml') if not exists(container_path): raise InvalidEpub('No META-INF/container.xml in epub') with open(container_path, 'rb') as cf: container = safe_xml_fromstring(cf.read()) opf_files = container.xpath(( r'child::ocf:rootfiles/ocf:rootfile' '[@media-type="%s" and @full-path]'%guess_type('a.opf') ), namespaces={'ocf':OCF_NS} ) if not opf_files: raise InvalidEpub('META-INF/container.xml contains no link to OPF file') opf_path = os.path.join(self.root, *(urlunquote(opf_files[0].get('full-path')).split('/'))) if not exists(opf_path): raise InvalidEpub('OPF file does not exist at location pointed to' ' by META-INF/container.xml')

super().__init__(tdir, opf_path, log)

self.obfuscated_fonts = {} if 'META-INF/encryption.xml' in self.name_path_map: self.process_encryption() self.parsed_cache['META-INF/container.xml'] = container def clone_data(self, dest_dir): ans = super().clone_data(dest_dir) ans['pathtoepub'] = self.pathtoepub ans['obfuscated_fonts'] = self.obfuscated_fonts.copy() ans['is_dir'] = self.is_dir return ans def rename(self, old_name, new_name): is_opf = old_name == self.opf_name super().rename(old_name, new_name) if is_opf: for elem in self.parsed('META-INF/container.xml').xpath(( r'child::ocf:rootfiles/ocf:rootfile' '[@media-type="%s" and @full-path]'%guess_type('a.opf') ), namespaces={'ocf':OCF_NS} ): # The asinine epubcheck cannot handle quoted filenames in # container.xml elem.set('full-path', self.opf_name) self.dirty('META-INF/container.xml') if old_name in self.obfuscated_fonts: self.obfuscated_fonts[new_name] = self.obfuscated_fonts.pop(old_name) enc = self.parsed('META-INF/encryption.xml') for cr in enc.xpath('//*[local-name()="CipherReference" and @URI]'): if self.href_to_name(cr.get('URI')) == old_name: cr.set('URI', self.name_to_href(new_name)) self.dirty('META-INF/encryption.xml')

@property def names_that_need_not_be_manifested(self): return super().names_that_need_not_be_manifested | {'META-INF/' + x for x in self.META_INF}

def ok_to_be_unmanifested(self, name): return name in self.names_that_need_not_be_manifested or name.startswith('META-INF/')

@property def names_that_must_not_be_removed(self): return super().names_that_must_not_be_removed | {'META-INF/container.xml'}

@property def names_that_must_not_be_changed(self): return super().names_that_must_not_be_changed | {'META-INF/' + x for x in self.META_INF}

def remove_item(self, name, remove_from_guide=True): # Handle removal of obfuscated fonts if name == 'META-INF/encryption.xml': self.obfuscated_fonts.clear() if name in self.obfuscated_fonts: self.obfuscated_fonts.pop(name, None) enc = self.parsed('META-INF/encryption.xml') for em in enc.xpath('//*[local-name()="EncryptionMethod" and @Algorithm]'): alg = em.get('Algorithm') if alg not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}: continue try: cr = em.getparent().xpath('descendant::*[local-name()="CipherReference" and @URI]')[0] except (IndexError, ValueError, KeyError): continue if name == self.href_to_name(cr.get('URI')): self.remove_from_xml(em.getparent()) self.dirty('META-INF/encryption.xml') super().remove_item(name, remove_from_guide=remove_from_guide)

def read_raw_unique_identifier(self): package_id = raw_unique_identifier = idpf_key = None for attrib, val in iteritems(self.opf.attrib): if attrib.endswith('unique-identifier'): package_id = val break if package_id is not None: for elem in self.opf_xpath('//*[@id=%s]'%escape_xpath_attr(package_id)): if elem.text: raw_unique_identifier = elem.text break if raw_unique_identifier is not None: idpf_key = raw_unique_identifier idpf_key = re.sub('[\u0020\u0009\u000d\u000a]', '', idpf_key) idpf_key = hashlib.sha1(idpf_key.encode('utf-8')).digest() return package_id, raw_unique_identifier, idpf_key def iter_encryption_entries(self): if 'META-INF/encryption.xml' in self.name_path_map: enc = self.parsed('META-INF/encryption.xml') for em in enc.xpath('//*[local-name()="EncryptionMethod" and @Algorithm]'): try: cr = em.getparent().xpath('descendant::*[local-name()="CipherReference" and @URI]')[0] except Exception: cr = None yield em, cr def process_encryption(self): fonts = {} for em, cr in self.iter_encryption_entries(): alg = em.get('Algorithm') if alg not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}: raise DRMError() if cr is None: continue name = self.href_to_name(cr.get('URI')) path = self.name_path_map.get(name, None) if path is not None: fonts[name] = alg package_id, raw_unique_identifier, idpf_key = self.read_raw_unique_identifier() key = None for item in self.opf_xpath('//*[local-name()="metadata"]/*' '[local-name()="identifier"]'): scheme = None for xkey in item.attrib.keys(): if xkey.endswith('scheme'): scheme = item.get(xkey) if (scheme and scheme.lower() == 'uuid') or \ (item.text and item.text.startswith('urn:uuid:')): try: key = item.text.rpartition(':')[-1] key = uuid.UUID(key).bytes except Exception: self.log.exception('Failed to parse obfuscation key') key = None for font, alg in iteritems(fonts): tkey = key if alg == ADOBE_OBFUSCATION else idpf_key if not tkey: raise ObfuscationKeyMissing('Failed to find obfuscation key') raw = self.raw_data(font, decode=False) raw = decrypt_font_data(tkey, raw, alg) with self.open(font, 'wb') as f: f.write(raw) self.obfuscated_fonts[font] = (alg, tkey)

def update_modified_timestamp(self): from calibre.ebooks.metadata.opf3 import set_last_modified_in_opf set_last_modified_in_opf(self.opf) self.dirty(self.opf_name)

def commit(self, outpath=None, keep_parsed=False): if self.opf_version_parsed.major == 3: self.update_modified_timestamp() super().commit(keep_parsed=keep_parsed) container_path = join(self.root, 'META-INF', 'container.xml') if not exists(container_path): raise InvalidEpub('No META-INF/container.xml in EPUB, this typically happens if the temporary files calibre' ' is using are deleted by some other program while calibre is running') restore_fonts = {} for name in self.obfuscated_fonts: if name not in self.name_path_map: continue alg, key = self.obfuscated_fonts[name] # Decrypting and encrypting are the same operation (XOR with key) restore_fonts[name] = data = self.raw_data(name, decode=False) with self.open(name, 'wb') as f: f.write(decrypt_font_data(key, data, alg)) if outpath is None: outpath = self.pathtoepub if self.is_dir: # First remove items from the source dir that do not exist any more for is_root, dirpath, fname in walk_dir(self.pathtoepub): if fname is not None: if is_root and fname == 'mimetype': continue base = self.root if is_root else os.path.join(self.root, os.path.relpath(dirpath, self.pathtoepub)) fpath = os.path.join(base, fname) if not os.path.exists(fpath): os.remove(os.path.join(dirpath, fname)) try: os.rmdir(dirpath) except OSError as err: if err.errno != errno.ENOTEMPTY: raise # Now copy over everything from root to source dir for dirpath, dirnames, filenames in os.walk(self.root): is_root = os.path.abspath(os.path.normcase(dirpath)) == os.path.abspath(os.path.normcase(self.root)) base = self.pathtoepub if is_root else os.path.join(self.pathtoepub, os.path.relpath(dirpath, self.root)) try: os.mkdir(base) except OSError as err: if err.errno != errno.EEXIST: raise for fname in filenames: with open(os.path.join(dirpath, fname), 'rb') as src, open(os.path.join(base, fname), 'wb') as dest: shutil.copyfileobj(src, dest)

else: from calibre.ebooks.tweak import zip_rebuilder with open(join(self.root, 'mimetype'), 'wb') as f: et = guess_type('a.epub') if not isinstance(et, bytes): et = et.encode('ascii') f.write(et) zip_rebuilder(self.root, outpath) for name, data in iteritems(restore_fonts): with self.open(name, 'wb') as f: f.write(data)

@property def path_to_ebook(self): return self.pathtoepub

@path_to_ebook.setter def path_to_ebook(self, val): self.pathtoepub = val

# }}}

# AZW3 {{{ class InvalidMobi(InvalidBook): pass def do_explode(path, dest): from calibre.ebooks.mobi.reader.mobi6 import MobiReader from calibre.ebooks.mobi.reader.mobi8 import Mobi8Reader with open(path, 'rb') as stream: mr = MobiReader(stream, default_log, None, None)

with CurrentDir(dest): mr = Mobi8Reader(mr, default_log, for_tweak=True) opf = os.path.abspath(mr()) obfuscated_fonts = mr.encrypted_fonts return opf, obfuscated_fonts def opf_to_azw3(opf, outpath, container): from calibre.ebooks.conversion.plumber import Plumber, create_oebbook from calibre.ebooks.mobi.tweak import set_cover class Item(Manifest.Item):

def _parse_css(self, data): # The default CSS parser used by oeb.base inserts the h namespace # and resolves all @import rules. We dont want that. return container.parse_css(data)

def specialize(oeb): oeb.manifest.Item = Item plumber = Plumber(opf, outpath, container.log) plumber.setup_options() inp = plugin_for_input_format('azw3') outp = plugin_for_output_format('azw3') plumber.opts.mobi_passthrough = True plumber.opts.keep_ligatures = True oeb = create_oebbook(container.log, opf, plumber.opts, specialize=specialize) set_cover(oeb) outp.convert(oeb, outpath, inp, plumber.opts, container.log)

def epub_to_azw3(epub, outpath=None): container = get_container(epub, tweak_mode=True) changed = False for item in container.opf_xpath('//opf:manifest/opf:item[@properties and @href]'): p = item.get('properties').split() if 'cover-image' in p: href = item.get('href') guides = container.opf_xpath('//opf:guide') if not guides: guides = (container.opf.makeelement(OPF('guide')),) container.opf.append(guides[0]) for guide in guides: for child in guide: if child.get('type') == 'cover': break else: guide.append(guide.makeelement(OPF('reference'), type='cover', href=href)) changed = True break elif 'calibre:title-page' in p: item.getparent().remove(item) if changed: container.dirty(container.opf_name) container.commit_item(container.opf_name) outpath = outpath or (epub.rpartition('.')[0] + '.azw3') opf_to_azw3(container.name_to_abspath(container.opf_name), outpath, container)

class AZW3Container(Container):

book_type = 'azw3' SUPPORTS_TITLEPAGES = False SUPPORTS_FILENAMES = False def __init__(self, pathtoazw3, log, clone_data=None, tdir=None): if clone_data is not None: super().__init__(None, None, log, clone_data=clone_data) for x in ('pathtoazw3', 'obfuscated_fonts'): setattr(self, x, clone_data[x]) return self.pathtoazw3 = pathtoazw3 if tdir is None: tdir = PersistentTemporaryDirectory('_azw3_container') tdir = os.path.abspath(os.path.realpath(tdir)) self.root = tdir with open(pathtoazw3, 'rb') as stream: raw = stream.read(3) if raw == b'TPZ': raise InvalidMobi(_('This is not a MOBI file. It is a Topaz file.'))

try: header = MetadataHeader(stream, default_log) except MobiError: raise InvalidMobi(_('This is not a MOBI file.'))

if header.encryption_type != 0: raise DRMError()

kf8_type = header.kf8_type if kf8_type is None: raise InvalidMobi(_('This MOBI file does not contain a KF8 format ' 'book. KF8 is the new format from Amazon. calibre can ' 'only edit MOBI files that contain KF8 books. Older ' 'MOBI files without KF8 are not editable.'))

if kf8_type == 'joint': raise InvalidMobi(_('This MOBI file contains both KF8 and ' 'older Mobi6 data. calibre can only edit MOBI files ' 'that contain only KF8 data.'))

try: opf_path, obfuscated_fonts = fork_job( 'calibre.ebooks.oeb.polish.container', 'do_explode', args=(pathtoazw3, tdir), no_output=True)['result'] except WorkerError as e: log(e.orig_tb) raise InvalidMobi('Failed to explode MOBI') super().__init__(tdir, opf_path, log) self.obfuscated_fonts = {x.replace(os.sep, '/') for x in obfuscated_fonts}

def clone_data(self, dest_dir): ans = super().clone_data(dest_dir) ans['pathtoazw3'] = self.pathtoazw3 ans['obfuscated_fonts'] = self.obfuscated_fonts.copy() return ans def commit(self, outpath=None, keep_parsed=False): super().commit(keep_parsed=keep_parsed) if outpath is None: outpath = self.pathtoazw3 opf_to_azw3(self.name_path_map[self.opf_name], outpath, self)

@property def path_to_ebook(self): return self.pathtoazw3

@path_to_ebook.setter def path_to_ebook(self, val): self.pathtoazw3 = val

@property def names_that_must_not_be_changed(self): return set(self.name_path_map) # }}}

def get_container(path, log=None, tdir=None, tweak_mode=False): if log is None: log = default_log try: isdir = os.path.isdir(path) except Exception: isdir = False own_tdir = not tdir ebook_cls = (AZW3Container if path.rpartition('.')[-1].lower() in {'azw3', 'mobi', 'original_azw3', 'original_mobi'} and not isdir else EpubContainer) if own_tdir: tdir = PersistentTemporaryDirectory(f'_{ebook_cls.book_type}_container') try: ebook = ebook_cls(path, log, tdir=tdir) ebook.tweak_mode = tweak_mode except BaseException: if own_tdir: shutil.rmtree(tdir, ignore_errors=True) raise return ebook def test_roundtrip(): ebook = get_container(sys.argv[-1]) p = PersistentTemporaryFile(suffix='.'+sys.argv[-1].rpartition('.')[-1]) p.close() ebook.commit(outpath=p.name) ebook2 = get_container(p.name) ebook3 = get_container(p.name) diff = ebook3.compare_to(ebook2) if diff is not None: print(diff)

if __name__ == '__main__': test_roundtrip()

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.cover

calibre.ebooks.oeb.polish.cover öğesinin kaynak kodu

__license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import os import re import shutil from calibre.ebooks.oeb.base import OEB_DOCS, OPF, XLINK, XPath, xml2text from calibre.ebooks.oeb.polish.replace import get_recommended_folders, replace_links from calibre.utils.imghdr import identify from polyglot.builtins import iteritems def set_azw3_cover(container, cover_path, report, options=None): existing_image = options is not None and options.get('existing_image', False) name = None found = True for gi in container.opf_xpath('//opf:guide/opf:reference[@href and contains(@type, "cover")]'): href = gi.get('href') name = container.href_to_name(href, container.opf_name) container.remove_from_xml(gi) if existing_image: name = cover_path found = False else: if name is None or not container.has_name(name): item = container.generate_item(name='cover.jpeg', id_prefix='cover') name = container.href_to_name(item.get('href'), container.opf_name) found = False href = container.name_to_href(name, container.opf_name) guide = container.opf_xpath('//opf:guide')[0] container.insert_into_xml(guide, guide.makeelement( OPF('reference'), href=href, type='cover')) if not existing_image: with open(cover_path, 'rb') as src, container.open(name, 'wb') as dest: shutil.copyfileobj(src, dest) container.dirty(container.opf_name) report(_('Cover updated') if found else _('Cover inserted'))

def get_azw3_raster_cover_name(container): items = container.opf_xpath('//opf:guide/opf:reference[@href and contains(@type, "cover")]') if items: return container.href_to_name(items[0].get('href'))

def mark_as_cover_azw3(container, name): href = container.name_to_href(name, container.opf_name) found = False for item in container.opf_xpath('//opf:guide/opf:reference[@href and contains(@type, "cover")]'): item.set('href', href) found = True if not found: for guide in container.opf_xpath('//opf:guide'): container.insert_into_xml(guide, guide.makeelement( OPF('reference'), href=href, type='cover')) container.dirty(container.opf_name)

def get_raster_cover_name(container): if container.book_type == 'azw3': return get_azw3_raster_cover_name(container) return find_cover_image(container, strict=True)

def get_cover_page_name(container): if container.book_type == 'azw3': return return find_cover_page(container)

[belgeler] def set_cover(container, cover_path, report=None, options=None): ''' Set the cover of the book to the image pointed to by cover_path.

:param cover_path: Either the absolute path to an image file or the canonical name of an image in the book. When using an image in the book, you must also set options, see below. :param report: An optional callable that takes a single argument. It will be called with information about the tasks being processed. :param options: None or a dictionary that controls how the cover is set. The dictionary can have entries: **keep_aspect**: True or False (Preserve aspect ratio of covers in EPUB) **no_svg**: True or False (Use an SVG cover wrapper in the EPUB titlepage) **existing**: True or False (``cover_path`` refers to an existing image in the book) ''' report = report or (lambda x:x) if container.book_type == 'azw3': set_azw3_cover(container, cover_path, report, options=options) else: set_epub_cover(container, cover_path, report, options=options)

[belgeler] def mark_as_cover(container, name): ''' Mark the specified image as the cover image. ''' if name not in container.mime_map: raise ValueError('Cannot mark %s as cover as it does not exist' % name) mt = container.mime_map[name] if not is_raster_image(mt): raise ValueError('Cannot mark %s as the cover image as it is not a raster image' % name) if container.book_type == 'azw3': mark_as_cover_azw3(container, name) else: mark_as_cover_epub(container, name)

############################################################################### # The delightful EPUB cover processing def is_raster_image(media_type): return media_type and media_type.lower() in { 'image/png', 'image/jpeg', 'image/jpg', 'image/gif'}

COVER_TYPES = { 'coverimagestandard', 'other.ms-coverimage-standard', 'other.ms-titleimage-standard', 'other.ms-titleimage', 'other.ms-coverimage', 'other.ms-thumbimage-standard', 'other.ms-thumbimage', 'thumbimagestandard', 'cover'}

def find_cover_image2(container, strict=False): manifest_id_map = container.manifest_id_map mm = container.mime_map for meta in container.opf_xpath('//opf:meta[@name="cover" and @content]'): item_id = meta.get('content') name = manifest_id_map.get(item_id, None) media_type = mm.get(name, None) if is_raster_image(media_type): return name

# First look for a guide item with type == 'cover' guide_type_map = container.guide_type_map for ref_type, name in iteritems(guide_type_map): if ref_type.lower() == 'cover' and is_raster_image(mm.get(name, None)): return name if strict: return

# Find the largest image from all possible guide cover items largest_cover = (None, 0) for ref_type, name in iteritems(guide_type_map): if ref_type.lower() in COVER_TYPES and is_raster_image(mm.get(name, None)): path = container.name_path_map.get(name, None) if path: sz = os.path.getsize(path) if sz > largest_cover[1]: largest_cover = (name, sz)

if largest_cover[0]: return largest_cover[0]

def find_cover_image3(container): for name in container.manifest_items_with_property('cover-image'): return name manifest_id_map = container.manifest_id_map mm = container.mime_map for meta in container.opf_xpath('//opf:meta[@name="cover" and @content]'): item_id = meta.get('content') name = manifest_id_map.get(item_id, None) media_type = mm.get(name, None) if is_raster_image(media_type): return name def find_cover_image(container, strict=False): 'Find a raster image marked as a cover in the OPF' ver = container.opf_version_parsed if ver.major < 3: return find_cover_image2(container, strict=strict) else: return find_cover_image3(container)

def get_guides(container): guides = container.opf_xpath('//opf:guide') if not guides: container.insert_into_xml(container.opf, container.opf.makeelement( OPF('guide'))) guides = container.opf_xpath('//opf:guide') return guides def mark_as_cover_epub(container, name): mmap = {v:k for k, v in iteritems(container.manifest_id_map)} if name not in mmap: raise ValueError('Cannot mark %s as cover as it is not in manifest' % name) mid = mmap[name] ver = container.opf_version_parsed

# Remove all entries from the opf that identify a raster image as cover for meta in container.opf_xpath('//opf:meta[@name="cover" and @content]'): container.remove_from_xml(meta) for ref in container.opf_xpath('//opf:guide/opf:reference[@href and @type]'): if ref.get('type').lower() not in COVER_TYPES: continue rname = container.href_to_name(ref.get('href'), container.opf_name) mt = container.mime_map.get(rname, None) if is_raster_image(mt): container.remove_from_xml(ref)

if ver.major < 3: # Add reference to image in <metadata> for metadata in container.opf_xpath('//opf:metadata'): m = metadata.makeelement(OPF('meta'), name='cover', content=mid) container.insert_into_xml(metadata, m)

# If no entry for cover exists in guide, insert one that points to this # image if not container.opf_xpath('//opf:guide/opf:reference[@type="cover"]'): for guide in get_guides(container): container.insert_into_xml(guide, guide.makeelement( OPF('reference'), type='cover', href=container.name_to_href(name, container.opf_name))) else: container.apply_unique_properties(name, 'cover-image')

container.dirty(container.opf_name)

[belgeler] def mark_as_titlepage(container, name, move_to_start=True): ''' Mark the specified HTML file as the titlepage of the EPUB.

:param move_to_start: If True the HTML file is moved to the start of the spine ''' ver = container.opf_version_parsed if move_to_start: for item, q, linear in container.spine_iter: if name == q: break if not linear: item.set('linear', 'yes') if item.getparent().index(item) > 0: container.insert_into_xml(item.getparent(), item, 0) if ver.major < 3: for ref in container.opf_xpath('//opf:guide/opf:reference[@type="cover"]'): ref.getparent().remove(ref)

for guide in get_guides(container): container.insert_into_xml(guide, guide.makeelement( OPF('reference'), type='cover', href=container.name_to_href(name, container.opf_name))) else: container.apply_unique_properties(name, 'calibre:title-page')

container.dirty(container.opf_name)

def find_cover_page(container): 'Find a document marked as a cover in the OPF' ver = container.opf_version_parsed mm = container.mime_map if ver.major < 3: guide_type_map = container.guide_type_map for ref_type, name in iteritems(guide_type_map): if ref_type.lower() == 'cover' and mm.get(name, '').lower() in OEB_DOCS: return name else: for name in container.manifest_items_with_property('calibre:title-page'): return name from calibre.ebooks.oeb.polish.toc import get_landmarks for landmark in get_landmarks(container): if landmark['type'] == 'cover' and mm.get(landmark['dest'], '').lower() in OEB_DOCS: return landmark['dest']

def fix_conversion_titlepage_links_in_nav(container): from calibre.ebooks.oeb.polish.toc import find_existing_nav_toc cover_page_name = find_cover_page(container) if not cover_page_name: return nav_page_name = find_existing_nav_toc(container) if not nav_page_name: return for elem in container.parsed(nav_page_name).xpath('//*[@data-calibre-removed-titlepage]'): elem.attrib.pop('data-calibre-removed-titlepage') elem.set('href', container.name_to_href(cover_page_name, nav_page_name)) container.dirty(nav_page_name)

def find_cover_image_in_page(container, cover_page): root = container.parsed(cover_page) body = XPath('//h:body')(root) if len(body) != 1: return body = body[0] images = [] for img in XPath('descendant::h:img[@src]|descendant::svg:svg/descendant::svg:image')(body): href = img.get('src') or img.get(XLINK('href')) if href: name = container.href_to_name(href, base=cover_page) images.append(name) text = re.sub(r'\s+', '', xml2text(body)) if text or len(images) > 1: # Document has more content than a single image return if images: return images[0]

def clean_opf(container): 'Remove all references to covers from the OPF' manifest_id_map = container.manifest_id_map for meta in container.opf_xpath('//opf:meta[@name="cover" and @content]'): name = manifest_id_map.get(meta.get('content', None), None) container.remove_from_xml(meta) if name and name in container.name_path_map: yield name gtm = container.guide_type_map for ref in container.opf_xpath('//opf:guide/opf:reference[@type]'): typ = ref.get('type', '') if typ.lower() in COVER_TYPES: container.remove_from_xml(ref) name = gtm.get(typ, None) if name and name in container.name_path_map: yield name ver = container.opf_version_parsed if ver.major > 2: removed_names = container.apply_unique_properties(None, 'cover-image', 'calibre:title-page')[0] for name in removed_names: yield name container.dirty(container.opf_name)

def create_epub_cover(container, cover_path, existing_image, options=None): from calibre.ebooks.conversion.config import load_defaults from calibre.ebooks.oeb.transforms.cover import CoverManager try: ext = cover_path.rpartition('.')[-1].lower() except Exception: ext = 'jpeg' cname, tname = 'cover.' + ext, 'titlepage.xhtml' recommended_folders = get_recommended_folders(container, (cname, tname))

if existing_image: raster_cover = existing_image manifest_id = {v:k for k, v in iteritems(container.manifest_id_map)}[existing_image] raster_cover_item = container.opf_xpath('//opf:manifest/*[@id="%s"]' % manifest_id)[0] else: folder = recommended_folders[cname] if folder: cname = folder + '/' + cname raster_cover_item = container.generate_item(cname, id_prefix='cover') raster_cover = container.href_to_name(raster_cover_item.get('href'), container.opf_name)

with container.open(raster_cover, 'wb') as dest: if callable(cover_path): cover_path('write_image', dest) else: with open(cover_path, 'rb') as src: shutil.copyfileobj(src, dest) if options is None: opts = load_defaults('epub_output') keep_aspect = opts.get('preserve_cover_aspect_ratio', False) no_svg = opts.get('no_svg_cover', False) else: keep_aspect = options.get('keep_aspect', False) no_svg = options.get('no_svg', False) if no_svg: style = 'style="height: 100%%"' templ = CoverManager.NONSVG_TEMPLATE.replace('__style__', style) has_svg = False else: if callable(cover_path): templ = (options or {}).get('template', CoverManager.SVG_TEMPLATE) has_svg = 'xlink:href' in templ else: width, height = 600, 800 has_svg = True try: if existing_image: width, height = identify(container.raw_data(existing_image, decode=False))[1:] else: with open(cover_path, 'rb') as csrc: width, height = identify(csrc)[1:] except: container.log.exception("Failed to get width and height of cover") ar = 'xMidYMid meet' if keep_aspect else 'none' templ = CoverManager.SVG_TEMPLATE.replace('__ar__', ar) templ = templ.replace('__viewbox__', '0 0 %d %d'%(width, height)) templ = templ.replace('__width__', str(width)) templ = templ.replace('__height__', str(height)) folder = recommended_folders[tname] if folder: tname = folder + '/' + tname titlepage_item = container.generate_item(tname, id_prefix='titlepage') titlepage = container.href_to_name(titlepage_item.get('href'), container.opf_name) raw = templ % container.name_to_href(raster_cover, titlepage) with container.open(titlepage, 'wb') as f: if not isinstance(raw, bytes): raw = raw.encode('utf-8') f.write(raw)

# We have to make sure the raster cover item has id="cover" for the moron # that wrote the Nook firmware if raster_cover_item.get('id') != 'cover': from calibre.ebooks.oeb.base import uuid_id newid = uuid_id() for item in container.opf_xpath('//*[@id="cover"]'): item.set('id', newid) for item in container.opf_xpath('//*[@idref="cover"]'): item.set('idref', newid) raster_cover_item.set('id', 'cover')

spine = container.opf_xpath('//opf:spine')[0] ref = spine.makeelement(OPF('itemref'), idref=titlepage_item.get('id')) container.insert_into_xml(spine, ref, index=0) ver = container.opf_version_parsed if ver.major < 3: guide = container.opf_get_or_create('guide') container.insert_into_xml(guide, guide.makeelement( OPF('reference'), type='cover', title=_('Cover'), href=container.name_to_href(titlepage, base=container.opf_name))) metadata = container.opf_get_or_create('metadata') meta = metadata.makeelement(OPF('meta'), name='cover') meta.set('content', raster_cover_item.get('id')) container.insert_into_xml(metadata, meta) else: container.apply_unique_properties(raster_cover, 'cover-image') container.apply_unique_properties(titlepage, 'calibre:title-page') if has_svg: container.add_properties(titlepage, 'svg')

return raster_cover, titlepage def remove_cover_image_in_page(container, page, cover_images): for img in container.parsed(page).xpath('//*[local-name()="img" and @src]'): href = img.get('src') name = container.href_to_name(href, page) if name in cover_images: img.getparent().remove(img) break def has_epub_cover(container): if find_cover_image(container): return True if find_cover_page(container): return True spine_items = tuple(container.spine_items) if spine_items: candidate = container.abspath_to_name(spine_items[0]) if find_cover_image_in_page(container, candidate) is not None: return True return False def set_epub_cover(container, cover_path, report, options=None, image_callback=None): existing_image = options is not None and options.get('existing_image', False) if existing_image: existing_image = cover_path cover_image = find_cover_image(container) cover_page = find_cover_page(container) wrapped_image = extra_cover_page = None updated = False log = container.log possible_removals = set(clean_opf(container)) possible_removals # TODO: Handle possible_removals and also iterate over links in the removed # pages and handle possibly removing stylesheets referred to by them.

image_callback_called = False spine_items = tuple(container.spine_items) if cover_page is None and spine_items: # Check if the first item in the spine is a simple cover wrapper candidate = container.abspath_to_name(spine_items[0]) if find_cover_image_in_page(container, candidate) is not None: cover_page = candidate if cover_page is not None: log('Found existing cover page') wrapped_image = find_cover_image_in_page(container, cover_page)

if len(spine_items) > 1: # Look for an extra cover page c = container.abspath_to_name(spine_items[1]) if c != cover_page: candidate = find_cover_image_in_page(container, c) if candidate and candidate in {wrapped_image, cover_image}: log('Found an extra cover page that is a simple wrapper, removing it') # This page has only a single image and that image is the # cover image, remove it. container.remove_item(c) extra_cover_page = c spine_items = spine_items[:1] + spine_items[2:] elif candidate is None: # Remove the cover image if it is the first image in this # page remove_cover_image_in_page(container, c, {wrapped_image, cover_image})

if wrapped_image is not None: # The cover page is a simple wrapper around a single cover image, # we can remove it safely. log(f'Existing cover page {cover_page} is a simple wrapper, removing it') container.remove_item(cover_page) if wrapped_image != existing_image: if image_callback is not None and not image_callback_called: image_callback(cover_image, wrapped_image) image_callback_called = True container.remove_item(wrapped_image) updated = True if image_callback is not None and not image_callback_called: image_callback_called = True image_callback(cover_image, wrapped_image) if cover_image and cover_image != wrapped_image: # Remove the old cover image if cover_image != existing_image: container.remove_item(cover_image)

# Insert the new cover raster_cover, titlepage = create_epub_cover(container, cover_path, existing_image, options=options)

report(_('Cover updated') if updated else _('Cover inserted'))

# Replace links to the old cover image/cover page link_sub = {s:d for s, d in iteritems({ cover_page:titlepage, wrapped_image:raster_cover, cover_image:raster_cover, extra_cover_page:titlepage}) if s is not None and s != d} if link_sub: replace_links(container, link_sub, frag_map=lambda x, y:None) return raster_cover, titlepage

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.css

calibre.ebooks.oeb.polish.css öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'

import re from collections import defaultdict from functools import partial from operator import itemgetter from css_parser.css import CSSRule, CSSStyleDeclaration from css_selectors import Select, SelectorError, SelectorSyntaxError, parse from calibre import force_unicode from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, XHTML, css_text from calibre.ebooks.oeb.normalize_css import normalize_filter_css, normalizers from calibre.ebooks.oeb.polish.pretty import pretty_script_or_style, pretty_xml_tree, serialize from calibre.utils.icu import lower as icu_lower from calibre.utils.icu import numeric_sort_key from calibre.utils.localization import ngettext from polyglot.builtins import iteritems, itervalues from polyglot.functools import lru_cache class SelectorStatus: any_used: bool = False any_unused: bool = False def mark_used_selectors(rules, log, select): ans = SelectorStatus() for rule in rules: for selector in rule.selectorList: if getattr(selector, 'calibre_used', False): ans.any_used = True continue try: if select.has_matches(selector.selectorText): selector.calibre_used = True ans.any_used = True else: ans.any_unused = True selector.calibre_used = False except SelectorError: # Cannot parse/execute this selector, be safe and assume it # matches something selector.calibre_used = True ans.any_used = True return ans def get_imported_sheets(name, container, sheets, recursion_level=10, sheet=None): ans = set() sheet = sheet or sheets[name] for rule in sheet.cssRules.rulesOfType(CSSRule.IMPORT_RULE): if rule.href: iname = container.href_to_name(rule.href, name) if iname in sheets: ans.add(iname) if recursion_level > 0: for imported_sheet in tuple(ans): ans |= get_imported_sheets(imported_sheet, container, sheets, recursion_level=recursion_level-1) ans.discard(name) return ans def merge_declarations(first, second): for prop in second.getProperties(): first.setProperty(prop)

def merge_identical_selectors(sheet): ' Merge rules that have identical selectors ' selector_map = defaultdict(list) for rule in sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE): selector_map[rule.selectorText].append(rule) remove = [] for rule_group in itervalues(selector_map): if len(rule_group) > 1: for i in range(1, len(rule_group)): merge_declarations(rule_group[0].style, rule_group[i].style) remove.append(rule_group[i]) for rule in remove: sheet.cssRules.remove(rule) return len(remove)

def merge_identical_properties(sheet): ' Merge rules having identical properties ' properties_map = defaultdict(list)

def declaration_key(declaration): return tuple(sorted( ((prop.name, prop.propertyValue.value) for prop in declaration.getProperties()), key=itemgetter(0) ))

for idx, rule in enumerate(sheet.cssRules): if rule.type == CSSRule.STYLE_RULE: properties_map[declaration_key(rule.style)].append((idx, rule))

removals = [] num_merged = 0 for rule_group in properties_map.values(): if len(rule_group) < 2: continue num_merged += len(rule_group) selectors = rule_group[0][1].selectorList seen = {s.selectorText for s in selectors} rules = iter(rule_group) next(rules) for idx, rule in rules: removals.append(idx) for s in rule.selectorList: q = s.selectorText if q not in seen: seen.add(q) selectors.append(s) for idx in sorted(removals, reverse=True): sheet.cssRules.pop(idx) return num_merged def remove_unused_selectors_and_rules(rules_container, rules, removal_stats): ans = SelectorStatus() for r in rules: removals = [] for i, sel in enumerate(r.selectorList): if getattr(sel, 'calibre_used', True): ans.any_used = True else: removals.append(i) if removals: ans.any_unused = True if len(removals) == len(r.selectorList): rules_container.remove(r) removal_stats['rules'] += 1 else: removal_stats['selectors'] += len(removals) for i in reversed(removals): del r.selectorList[i] return ans

[belgeler] def remove_unused_css( container, report=None, remove_unused_classes=False, merge_rules=False, merge_rules_with_identical_properties=False, remove_unreferenced_sheets=False, ): ''' Remove all unused CSS rules from the book. An unused CSS rule is one that does not match any actual content.

:param report: An optional callable that takes a single argument. It is called with information about the operations being performed. :param remove_unused_classes: If True, class attributes in the HTML that do not match any CSS rules are also removed. :param merge_rules: If True, rules with identical selectors are merged. :param merge_rules_with_identical_properties: If True, rules with identical properties are merged. :param remove_unreferenced_sheets: If True, stylesheets that are not referenced by any content are removed ''' report = report or (lambda x:x)

def safe_parse(name): try: return container.parsed(name) except TypeError: pass sheets = {name:safe_parse(name) for name, mt in iteritems(container.mime_map) if mt in OEB_STYLES} sheets = {k:v for k, v in iteritems(sheets) if v is not None} num_merged = num_rules_merged = 0 if merge_rules: for name, sheet in iteritems(sheets): num = merge_identical_selectors(sheet) if num: container.dirty(name) num_merged += num if merge_rules_with_identical_properties: for name, sheet in iteritems(sheets): num = merge_identical_properties(sheet) if num: container.dirty(name) num_rules_merged += num import_map = {name:get_imported_sheets(name, container, sheets) for name in sheets} unreferenced_sheets = set(sheets) if remove_unused_classes: class_map = {name:{icu_lower(x) for x in classes_in_rule_list(sheet.cssRules)} for name, sheet in iteritems(sheets)} style_rules = {name:tuple(sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE)) for name, sheet in iteritems(sheets)}

removal_stats = {'rules': 0, 'selectors': 0} num_of_removed_classes = 0 for name, mt in iteritems(container.mime_map): if mt not in OEB_DOCS: continue root = container.parsed(name) select = Select(root, ignore_inappropriate_pseudo_classes=True) used_classes = set() for style in root.xpath('//*[local-name()="style"]'): if style.get('type', 'text/css') == 'text/css' and style.text: sheet = container.parse_css(style.text) if merge_rules: num = merge_identical_selectors(sheet) if num: num_merged += num container.dirty(name) if merge_rules_with_identical_properties: num = merge_identical_properties(sheet) if num: num_rules_merged += num container.dirty(name) if remove_unused_classes: used_classes |= {icu_lower(x) for x in classes_in_rule_list(sheet.cssRules)} imports = get_imported_sheets(name, container, sheets, sheet=sheet) for imported_sheet in imports: unreferenced_sheets.discard(imported_sheet) mark_used_selectors(style_rules[imported_sheet], container.log, select) if remove_unused_classes: used_classes |= class_map[imported_sheet] rules = tuple(sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE)) if mark_used_selectors(rules, container.log, select).any_unused: remove_unused_selectors_and_rules(sheet.cssRules, rules, removal_stats) style.text = force_unicode(sheet.cssText, 'utf-8') pretty_script_or_style(container, style) container.dirty(name)

for link in root.xpath('//*[local-name()="link" and @href]'): sname = container.href_to_name(link.get('href'), name) if sname not in sheets: continue mark_used_selectors(style_rules[sname], container.log, select) if remove_unused_classes: used_classes |= class_map[sname] unreferenced_sheets.discard(sname)

for iname in import_map[sname]: unreferenced_sheets.discard(iname) mark_used_selectors(style_rules[iname], container.log, select) if remove_unused_classes: used_classes |= class_map[iname]

if remove_unused_classes: for elem in root.xpath('//*[@class]'): original_classes, classes = elem.get('class', '').split(), [] for x in original_classes: if icu_lower(x) in used_classes: classes.append(x) if len(classes) != len(original_classes): if classes: elem.set('class', ' '.join(classes)) else: del elem.attrib['class'] num_of_removed_classes += len(original_classes) - len(classes) container.dirty(name)

for name, sheet in iteritems(sheets): if name in unreferenced_sheets: continue q = remove_unused_selectors_and_rules(sheet.cssRules, style_rules[name], removal_stats) if q.any_unused: container.dirty(name) num_sheets_removed = 0 if remove_unreferenced_sheets and len(unreferenced_sheets): num_sheets_removed += len(unreferenced_sheets) for uname in unreferenced_sheets: container.remove_item(uname)

num_changes = num_merged + num_of_removed_classes + num_rules_merged + removal_stats['rules'] + removal_stats['selectors'] + num_sheets_removed if num_changes > 0: if removal_stats['rules']: report(ngettext('Removed one unused CSS style rule', 'Removed {} unused CSS style rules', removal_stats['rules']).format(removal_stats['rules'])) if removal_stats['selectors']: report(ngettext('Removed one unused CSS selector', 'Removed {} unused CSS selectors', removal_stats['selectors']).format(removal_stats['selectors'])) if num_of_removed_classes > 0: report(ngettext('Removed one unused class from the HTML', 'Removed {} unused classes from the HTML', num_of_removed_classes).format(num_of_removed_classes)) if num_merged > 0: report(ngettext('Merged one CSS style rule with identical selectors', 'Merged {} CSS style rules with identical selectors', num_merged).format(num_merged)) if num_rules_merged > 0: report(ngettext('Merged one CSS style rule with identical properties', 'Merged {} CSS style rules with identical properties', num_rules_merged).format(num_rules_merged)) if num_sheets_removed: report(ngettext('Removed one unreferenced stylesheet', 'Removed {} unreferenced stylesheets', num_sheets_removed).format(num_sheets_removed)) if not removal_stats['rules']: report(_('No unused CSS style rules found')) if not removal_stats['selectors']: report(_('No unused CSS selectors found')) if remove_unused_classes and num_of_removed_classes == 0: report(_('No unused class attributes found')) if merge_rules and num_merged == 0: report(_('No style rules that could be merged found')) if remove_unreferenced_sheets and num_sheets_removed == 0: report(_('No unused stylesheets found')) return num_changes > 0def filter_declaration(style, properties=()): changed = False for prop in properties: if style.removeProperty(prop) != '': changed = True all_props = set(style.keys()) for prop in style.getProperties(): n = normalizers.get(prop.name, None) if n is not None: normalized = n(prop.name, prop.propertyValue) removed = properties.intersection(set(normalized)) if removed: changed = True style.removeProperty(prop.name) for prop in set(normalized) - removed - all_props: style.setProperty(prop, normalized[prop]) return changed def filter_sheet(sheet, properties=()): from css_parser.css import CSSRule changed = False remove = [] for rule in sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE): if filter_declaration(rule.style, properties): changed = True if rule.style.length == 0: remove.append(rule) for rule in remove: sheet.cssRules.remove(rule) return changed def transform_inline_styles(container, name, transform_sheet, transform_style): root = container.parsed(name) changed = False for style in root.xpath('//*[local-name()="style"]'): if style.text and (style.get('type') or 'text/css').lower() == 'text/css': sheet = container.parse_css(style.text) if transform_sheet(sheet): changed = True style.text = force_unicode(sheet.cssText, 'utf-8') pretty_script_or_style(container, style) for elem in root.xpath('//*[@style]'): text = elem.get('style', None) if text: style = container.parse_css(text, is_declaration=True) if transform_style(style): changed = True if style.length == 0: del elem.attrib['style'] else: elem.set('style', force_unicode(style.getCssText(separator=' '), 'utf-8')) return changed def transform_css(container, transform_sheet=None, transform_style=None, names=()): if not names: types = OEB_STYLES | OEB_DOCS names = [] for name, mt in iteritems(container.mime_map): if mt in types: names.append(name)

doc_changed = False for name in names: mt = container.mime_map[name] if mt in OEB_STYLES: sheet = container.parsed(name) if transform_sheet(sheet): container.dirty(name) doc_changed = True elif mt in OEB_DOCS: if transform_inline_styles(container, name, transform_sheet, transform_style): container.dirty(name) doc_changed = True return doc_changed

[belgeler] def filter_css(container, properties, names=()): ''' Remove the specified CSS properties from all CSS rules in the book.

:param properties: Set of properties to remove. For example: :code:`{'font-family', 'color'}`. :param names: The files from which to remove the properties. Defaults to all HTML and CSS files in the book. ''' properties = normalize_filter_css(properties) return transform_css(container, transform_sheet=partial(filter_sheet, properties=properties), transform_style=partial(filter_declaration, properties=properties), names=names)

def _classes_in_selector(selector, classes): for attr in ('selector', 'subselector', 'parsed_tree'): s = getattr(selector, attr, None) if s is not None: _classes_in_selector(s, classes) cn = getattr(selector, 'class_name', None) if cn is not None: classes.add(cn)

@lru_cache(maxsize=4096) def classes_in_selector(text): classes = set() try: for selector in parse(text): _classes_in_selector(selector, classes) except SelectorSyntaxError: pass return classes def classes_in_rule_list(css_rules): classes = set() for rule in css_rules: if rule.type == rule.STYLE_RULE: classes |= classes_in_selector(rule.selectorText) elif hasattr(rule, 'cssRules'): classes |= classes_in_rule_list(rule.cssRules) return classes def iter_declarations(sheet_or_rule): if hasattr(sheet_or_rule, 'cssRules'): for rule in sheet_or_rule.cssRules: yield from iter_declarations(rule) elif hasattr(sheet_or_rule, 'style'): yield sheet_or_rule.style elif isinstance(sheet_or_rule, CSSStyleDeclaration): yield sheet_or_rule def remove_property_value(prop, predicate): ''' Remove the Values that match the predicate from this property. If all values of the property would be removed, the property is removed from its parent instead. Note that this means the property must have a parent (a CSSStyleDeclaration). ''' removed_vals = list(filter(predicate, prop.propertyValue)) if len(removed_vals) == len(prop.propertyValue): prop.parent.removeProperty(prop.name) else: x = css_text(prop.propertyValue) for v in removed_vals: x = x.replace(css_text(v), '').strip() prop.propertyValue.cssText = x return bool(removed_vals)

RULE_PRIORITIES = {t:i for i, t in enumerate((CSSRule.COMMENT, CSSRule.CHARSET_RULE, CSSRule.IMPORT_RULE, CSSRule.NAMESPACE_RULE))}

def sort_sheet(container, sheet_or_text): ''' Sort the rules in a stylesheet. Note that in the general case this can change the effective styles, but for most common sheets, it should be safe. ''' sheet = container.parse_css(sheet_or_text) if isinstance(sheet_or_text, str) else sheet_or_text def text_sort_key(x): return numeric_sort_key(str(x or ''))

def selector_sort_key(x): return (x.specificity, text_sort_key(x.selectorText))

def rule_sort_key(rule): primary = RULE_PRIORITIES.get(rule.type, len(RULE_PRIORITIES)) secondary = text_sort_key(getattr(rule, 'atkeyword', '') or '') tertiary = None if rule.type == CSSRule.STYLE_RULE: primary += 1 selectors = sorted(rule.selectorList, key=selector_sort_key) tertiary = selector_sort_key(selectors[0]) rule.selectorText = ', '.join(s.selectorText for s in selectors) elif rule.type == CSSRule.FONT_FACE_RULE: try: tertiary = text_sort_key(rule.style.getPropertyValue('font-family')) except Exception: pass return primary, secondary, tertiary sheet.cssRules.sort(key=rule_sort_key) return sheet def add_stylesheet_links(container, name, text): root = container.parse_xhtml(text, name) head = root.xpath('//*[local-name() = "head"]') if not head: return head = head[0] sheets = tuple(container.manifest_items_of_type(lambda mt: mt in OEB_STYLES)) if not sheets: return for sname in sheets: link = head.makeelement(XHTML('link'), type='text/css', rel='stylesheet', href=container.name_to_href(sname, name)) head.append(link) pretty_xml_tree(head) return serialize(root, 'text/html')

def rename_class_in_rule_list(css_rules, old_name, new_name): # this regex will not match class names inside attribute value selectors # and it will match id selectors that contain .old_name but its the best # that can be done without implementing a full parser for CSS selectors pat = re.compile(rf'(?<=\.){re.escape(old_name)}(?:\W|$)')

def repl(m): return m.group().replace(old_name, new_name)

changed = False for rule in css_rules: if rule.type == rule.STYLE_RULE: old = rule.selectorText q = pat.sub(repl, old) if q != old: changed = True rule.selectorText = q elif hasattr(rule, 'cssRules'): if rename_class_in_rule_list(rule.cssRules, old_name, new_name): changed = True return changed def rename_class_in_doc(container, root, old_name, new_name): changed = False pat = re.compile(rf'(?:^|\W){re.escape(old_name)}(?:\W|$)')

def repl(m): return m.group().replace(old_name, new_name)

for elem in root.xpath('//*[@class]'): old = elem.get('class') if old: new = pat.sub(repl, old) if new != old: changed = True elem.set('class', new) for style in root.xpath('//*[local-name()="style"]'): if style.get('type', 'text/css') == 'text/css' and style.text: sheet = container.parse_css(style.text) if rename_class_in_rule_list(sheet.cssRules, old_name, new_name): changed = True style.text = force_unicode(sheet.cssText, 'utf-8') return changed def rename_class(container, old_name, new_name): changed = False if not old_name or old_name == new_name: return changed for sheet_name in container.manifest_items_of_type(lambda mt: mt in OEB_STYLES): sheet = container.parsed(sheet_name) if rename_class_in_rule_list(sheet.cssRules, old_name, new_name): container.dirty(sheet_name) changed = True for doc_name in container.manifest_items_of_type(lambda mt: mt in OEB_DOCS): doc = container.parsed(doc_name) if rename_class_in_doc(container, doc, old_name, new_name): container.dirty(doc_name) changed = True return changed

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.fonts

calibre.ebooks.oeb.polish.fonts öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'

from tinycss.fonts3 import parse_font, parse_font_family, serialize_font, serialize_font_family from calibre.ebooks.oeb.base import css_text from calibre.ebooks.oeb.normalize_css import normalize_font from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES from polyglot.builtins import iteritems def unquote(x): if x and len(x) > 1 and x[0] == x[-1] and x[0] in ('"', "'"): x = x[1:-1] return x def font_family_data_from_declaration(style, families): font_families = [] f = style.getProperty('font') if f is not None: f = normalize_font(f.propertyValue, font_family_as_list=True).get('font-family', None) if f is not None: font_families = [unquote(x) for x in f] f = style.getProperty('font-family') if f is not None: font_families = parse_font_family(css_text(f.propertyValue))

for f in font_families: families[f] = families.get(f, False)

def font_family_data_from_sheet(sheet, families): for rule in sheet.cssRules: if rule.type == rule.STYLE_RULE: font_family_data_from_declaration(rule.style, families) elif rule.type == rule.FONT_FACE_RULE: ff = rule.style.getProperty('font-family') if ff is not None: for f in parse_font_family(css_text(ff.propertyValue)): families[f] = True def font_family_data(container): families = {} for name, mt in iteritems(container.mime_map): if mt in OEB_STYLES: sheet = container.parsed(name) font_family_data_from_sheet(sheet, families) elif mt in OEB_DOCS: root = container.parsed(name) for style in root.xpath('//*[local-name() = "style"]'): if style.text and style.get('type', 'text/css').lower() == 'text/css': sheet = container.parse_css(style.text) font_family_data_from_sheet(sheet, families) for style in root.xpath('//*/@style'): if style: style = container.parse_css(style, is_declaration=True) font_family_data_from_declaration(style, families) return families def change_font_in_declaration(style, old_name, new_name=None): changed = False ff = style.getProperty('font-family') if ff is not None: fams = parse_font_family(css_text(ff.propertyValue)) nfams = list(filter(None, [new_name if x == old_name else x for x in fams])) if fams != nfams: if nfams: ff.propertyValue.cssText = serialize_font_family(nfams) else: style.removeProperty(ff.name) changed = True ff = style.getProperty('font') if ff is not None: props = parse_font(css_text(ff.propertyValue)) fams = props.get('font-family') or [] nfams = list(filter(None, [new_name if x == old_name else x for x in fams])) if fams != nfams: props['font-family'] = nfams if nfams: ff.propertyValue.cssText = serialize_font(props) else: style.removeProperty(ff.name) changed = True return changed def remove_embedded_font(container, sheet, rule, sheet_name): src = getattr(rule.style.getProperty('src'), 'value', None) if src is not None: if src.startswith('url('): src = src[4:-1] sheet.cssRules.remove(rule) if src: src = unquote(src) name = container.href_to_name(src, sheet_name) if container.has_name(name): container.remove_item(name)

def change_font_in_sheet(container, sheet, old_name, new_name, sheet_name): changed = False removals = [] for rule in sheet.cssRules: if rule.type == rule.STYLE_RULE: changed |= change_font_in_declaration(rule.style, old_name, new_name) elif rule.type == rule.FONT_FACE_RULE: ff = rule.style.getProperty('font-family') if ff is not None: families = {x for x in parse_font_family(css_text(ff.propertyValue))} if old_name in families: changed = True removals.append(rule) for rule in reversed(removals): remove_embedded_font(container, sheet, rule, sheet_name) return changed

[belgeler] def change_font(container, old_name, new_name=None): ''' Change a font family from old_name to new_name. Changes all occurrences of the font family in stylesheets, style tags and style attributes. If the old_name refers to an embedded font, it is removed. You can set new_name to None to remove the font family instead of changing it. ''' changed = False for name, mt in tuple(iteritems(container.mime_map)): if mt in OEB_STYLES: sheet = container.parsed(name) if change_font_in_sheet(container, sheet, old_name, new_name, name): container.dirty(name) changed = True elif mt in OEB_DOCS: root = container.parsed(name) for style in root.xpath('//*[local-name() = "style"]'): if style.text and style.get('type', 'text/css').lower() == 'text/css': sheet = container.parse_css(style.text) if change_font_in_sheet(container, sheet, old_name, new_name, name): container.dirty(name) changed = True for elem in root.xpath('//*[@style]'): style = elem.get('style', '') if style: style = container.parse_css(style, is_declaration=True) if change_font_in_declaration(style, old_name, new_name): style = css_text(style).strip().rstrip(';').strip() if style: elem.set('style', style) else: del elem.attrib['style'] container.dirty(name) changed = True return changed

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.jacket

calibre.ebooks.oeb.polish.jacket öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __docformat__ = 'restructuredtext en'

from calibre.customize.ui import output_profiles from calibre.ebooks.conversion.config import load_defaults from calibre.ebooks.oeb.base import OPF, XPath from calibre.ebooks.oeb.polish.cover import find_cover_page from calibre.ebooks.oeb.transforms.jacket import referenced_images from calibre.ebooks.oeb.transforms.jacket import render_jacket as render def render_jacket(container, jacket): mi = container.mi ps = load_defaults('page_setup') op = ps.get('output_profile', 'default') opmap = {x.short_name:x for x in output_profiles()} output_profile = opmap.get(op, opmap['default']) root = render(mi, output_profile) for img, path in referenced_images(root): container.log('Embedding referenced image: %s into jacket' % path) ext = path.rpartition('.')[-1] jacket_item = container.generate_item('jacket_image.'+ext, id_prefix='jacket_img') name = container.href_to_name(jacket_item.get('href'), container.opf_name) with open(path, 'rb') as f: container.parsed_cache[name] = f.read() container.commit_item(name) href = container.name_to_href(name, jacket) img.set('src', href) return root def is_legacy_jacket(root): return len(root.xpath( '//*[starts-with(@class,"calibrerescale") and (local-name()="h1" or local-name()="h2")]')) > 0 def is_current_jacket(root): return len(XPath( '//h:meta[@name="calibre-content" and @content="jacket"]')(root)) > 0 def find_existing_jacket(container): for item in container.spine_items: name = container.abspath_to_name(item) if container.book_type == 'azw3': root = container.parsed(name) if is_current_jacket(root): return name else: if name.rpartition('/')[-1].startswith('jacket') and name.endswith('.xhtml'): root = container.parsed(name) if is_current_jacket(root) or is_legacy_jacket(root): return name def replace_jacket(container, name): root = render_jacket(container, name) container.parsed_cache[name] = root container.dirty(name)

[belgeler] def remove_jacket(container): ' Remove an existing jacket, if any. Returns False if no existing jacket was found. ' name = find_existing_jacket(container) if name is not None: remove_jacket_images(container, name) container.remove_item(name) return True return False
def remove_jacket_images(container, name): root = container.parsed_cache[name] for img in root.xpath('//*[local-name() = "img" and @src]'): iname = container.href_to_name(img.get('src'), name) if container.has_name(iname): container.remove_item(iname)

[belgeler] def add_or_replace_jacket(container): ''' Either create a new jacket from the book's metadata or replace an existing jacket. Returns True if an existing jacket was replaced. ''' name = find_existing_jacket(container) found = True if name is None: jacket_item = container.generate_item('jacket.xhtml', id_prefix='jacket') name = container.href_to_name(jacket_item.get('href'), container.opf_name) found = False if found: remove_jacket_images(container, name)

replace_jacket(container, name) if not found: # Insert new jacket into spine index = 0 sp = container.abspath_to_name(next(container.spine_items)) if sp == find_cover_page(container): index = 1 itemref = container.opf.makeelement(OPF('itemref'), idref=jacket_item.get('id')) container.insert_into_xml(container.opf_xpath('//opf:spine')[0], itemref, index=index) return found

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.pretty

calibre.ebooks.oeb.polish.pretty öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'

import textwrap

# from lxml.etree import Element from calibre import force_unicode from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, SVG, XHTML, XPNSMAP, barename, serialize from calibre.ebooks.oeb.polish.container import OPF_NAMESPACES from calibre.ebooks.oeb.polish.utils import guess_type from calibre.utils.icu import sort_key from polyglot.builtins import iteritems def isspace(x): return not x.strip('\u0009\u000a\u000c\u000d\u0020')

def pretty_xml_tree(elem, level=0, indent=' '): ''' XML beautifier, assumes that elements that have children do not have textual content. Also assumes that there is no text immediately after closing tags. These are true for opf/ncx and container.xml files. If either of the assumptions are violated, there should be no data loss, but pretty printing won't produce optimal results.''' if (not elem.text and len(elem) > 0) or (elem.text and isspace(elem.text)): elem.text = '\n' + (indent * (level+1)) for i, child in enumerate(elem): pretty_xml_tree(child, level=level+1, indent=indent) if not child.tail or isspace(child.tail): l = level + 1 if i == len(elem) - 1: l -= 1 child.tail = '\n' + (indent * l)

def pretty_opf(root): # Put all dc: tags first starting with title and author. Preserve order for # the rest. def dckey(x): return {'title':0, 'creator':1}.get(barename(x.tag), 2) for metadata in root.xpath('//opf:metadata', namespaces=OPF_NAMESPACES): dc_tags = metadata.xpath('./*[namespace-uri()="%s"]' % OPF_NAMESPACES['dc']) dc_tags.sort(key=dckey) for x in reversed(dc_tags): metadata.insert(0, x)

# Group items in the manifest spine_ids = root.xpath('//opf:spine/opf:itemref/@idref', namespaces=OPF_NAMESPACES) spine_ids = {x:i for i, x in enumerate(spine_ids)}

def manifest_key(x): mt = x.get('media-type', '') href = x.get('href', '') ext = href.rpartition('.')[-1].lower() cat = 1000 if mt in OEB_DOCS: cat = 0 elif mt == guess_type('a.ncx'): cat = 1 elif mt in OEB_STYLES: cat = 2 elif mt.startswith('image/'): cat = 3 elif ext in {'otf', 'ttf', 'woff', 'woff2'}: cat = 4 elif mt.startswith('audio/'): cat = 5 elif mt.startswith('video/'): cat = 6 if cat == 0: i = spine_ids.get(x.get('id', None), 1000000000) else: i = sort_key(href) return (cat, i)

for manifest in root.xpath('//opf:manifest', namespaces=OPF_NAMESPACES): try: children = sorted(manifest, key=manifest_key) except AttributeError: continue # There are comments so dont sort since that would mess up the comments for x in reversed(children): manifest.insert(0, x)

SVG_TAG = SVG('svg') BLOCK_TAGS = frozenset(map(XHTML, ( 'address', 'article', 'aside', 'audio', 'blockquote', 'body', 'canvas', 'col', 'colgroup', 'dd', 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'li', 'noscript', 'ol', 'output', 'p', 'pre', 'script', 'section', 'style', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'ul', 'video', 'img'))) | {SVG_TAG}

def isblock(x): if callable(x.tag) or not x.tag: return True if x.tag in BLOCK_TAGS: return True return False def has_only_blocks(x): if hasattr(x.tag, 'split') and len(x) == 0: # Tag with no children, return False if x.text and not isspace(x.text): return False for child in x: if not isblock(child) or (child.tail and not isspace(child.tail)): return False return True def indent_for_tag(x): prev = x.getprevious() x = x.getparent().text if prev is None else prev.tail if not x: return '' s = x.rpartition('\n')[-1] return s if isspace(s) else ''

def set_indent(elem, attr, indent): x = getattr(elem, attr) if not x: x = indent else: lines = x.splitlines() if isspace(lines[-1]): lines[-1] = indent else: lines.append(indent) x = '\n'.join(lines) setattr(elem, attr, x)

def pretty_block(parent, level=1, indent=' '): ''' Surround block tags with blank lines and recurse into child block tags that contain only other block tags ''' if not parent.text or isspace(parent.text): parent.text = '' nn = '\n' if hasattr(parent.tag, 'strip') and barename(parent.tag) in {'tr', 'td', 'th'} else '\n\n' parent.text = parent.text + nn + (indent * level) for i, child in enumerate(parent): if isblock(child) and has_only_blocks(child): pretty_block(child, level=level+1, indent=indent) elif child.tag == SVG_TAG: pretty_xml_tree(child, level=level, indent=indent) l = level if i == len(parent) - 1: l -= 1 if not child.tail or isspace(child.tail): child.tail = '' child.tail = child.tail + nn + (indent * l)

def pretty_script_or_style(container, child): if child.text: indent = indent_for_tag(child) if child.tag.endswith('style'): child.text = force_unicode(pretty_css(container, '', child.text), 'utf-8') child.text = textwrap.dedent(child.text) child.text = '\n' + '\n'.join([(indent + x) if x else '' for x in child.text.splitlines()]) set_indent(child, 'text', indent)

def pretty_html_tree(container, root): root.text = '\n\n' for child in root: child.tail = '\n\n' if hasattr(child.tag, 'endswith') and child.tag.endswith('}head'): pretty_xml_tree(child) for body in root.findall('h:body', namespaces=XPNSMAP): pretty_block(body) # Special case the handling of a body that contains a single block tag # with all content. In this case we prettify the containing block tag # even if it has non block children. if (len(body) == 1 and not callable(body[0].tag) and isblock(body[0]) and not has_only_blocks( body[0]) and barename(body[0].tag) not in ( 'pre', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6') and len(body[0]) > 0): pretty_block(body[0], level=2)

if container is not None: # Handle <script> and <style> tags for child in root.xpath('//*[local-name()="script" or local-name()="style"]'): pretty_script_or_style(container, child)

[belgeler] def fix_html(container, raw): ' Fix any parsing errors in the HTML represented as a string in raw. Fixing is done using the HTML5 parsing algorithm. ' root = container.parse_xhtml(raw) return serialize(root, 'text/html')

[belgeler] def pretty_html(container, name, raw): ' Pretty print the HTML represented as a string in raw ' root = container.parse_xhtml(raw) pretty_html_tree(container, root) return serialize(root, 'text/html')

[belgeler] def pretty_css(container, name, raw): ' Pretty print the CSS represented as a string in raw ' sheet = container.parse_css(raw) return serialize(sheet, 'text/css')

[belgeler] def pretty_xml(container, name, raw): ' Pretty print the XML represented as a string in raw. If ``name`` is the name of the OPF, extra OPF-specific prettying is performed. ' root = container.parse_xml(raw) if name == container.opf_name: pretty_opf(root) pretty_xml_tree(root) return serialize(root, 'text/xml')

[belgeler] def fix_all_html(container): ' Fix any parsing errors in all HTML files in the container. Fixing is done using the HTML5 parsing algorithm. ' for name, mt in iteritems(container.mime_map): if mt in OEB_DOCS: container.parsed(name) container.dirty(name)

[belgeler] def pretty_all(container): ' Pretty print all HTML/CSS/XML files in the container ' xml_types = {guess_type('a.ncx'), guess_type('a.xml'), guess_type('a.svg')} for name, mt in iteritems(container.mime_map): prettied = False if mt in OEB_DOCS: pretty_html_tree(container, container.parsed(name)) prettied = True elif mt in OEB_STYLES: container.parsed(name) prettied = True elif name == container.opf_name: root = container.parsed(name) pretty_opf(root) pretty_xml_tree(root) prettied = True elif mt in xml_types: pretty_xml_tree(container.parsed(name)) prettied = True if prettied: container.dirty(name)

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.replace

calibre.ebooks.oeb.polish.replace öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import codecs import os import posixpath import shutil from collections import Counter, defaultdict from functools import partial from calibre import sanitize_file_name from calibre.ebooks.chardet import strip_encoding_declarations from calibre.ebooks.oeb.base import css_text from calibre.ebooks.oeb.polish.css import iter_declarations, remove_property_value from calibre.ebooks.oeb.polish.utils import extract from polyglot.builtins import iteritems, itervalues from polyglot.urllib import urlparse, urlunparse class LinkReplacer:

def __init__(self, base, container, link_map, frag_map): self.base = base self.frag_map = frag_map self.link_map = link_map self.container = container self.replaced = False def __call__(self, url): if url and url.startswith('#'): repl = self.frag_map(self.base, url[1:]) if not repl or repl == url[1:]: return url self.replaced = True return '#' + repl name = self.container.href_to_name(url, self.base) if not name: return url nname = self.link_map.get(name, None) if not nname: return url purl = urlparse(url) href = self.container.name_to_href(nname, self.base) if purl.fragment: nfrag = self.frag_map(name, purl.fragment) if nfrag: href += '#%s'%nfrag if href != url: self.replaced = True return href class IdReplacer:

def __init__(self, base, container, id_map): self.base, self.container, self.replaced = base, container, False self.id_map = id_map def __call__(self, url): if url and url.startswith('#'): repl = self.id_map.get(self.base, {}).get(url[1:]) if repl is None or repl == url[1:]: return url self.replaced = True return '#' + repl name = self.container.href_to_name(url, self.base) if not name: return url id_map = self.id_map.get(name) if id_map is None: return url purl = urlparse(url) nfrag = id_map.get(purl.fragment) if nfrag is None: return url purl = purl._replace(fragment=nfrag) href = urlunparse(purl) if href != url: self.replaced = True return href class LinkRebaser:

def __init__(self, container, old_name, new_name): self.old_name, self.new_name = old_name, new_name self.container = container self.replaced = False def __call__(self, url): if url and url.startswith('#'): return url purl = urlparse(url) frag = purl.fragment name = self.container.href_to_name(url, self.old_name) if not name: return url if name == self.old_name: name = self.new_name href = self.container.name_to_href(name, self.new_name) if frag: href += '#' + frag if href != url: self.replaced = True return href

[belgeler] def replace_links(container, link_map, frag_map=lambda name, frag:frag, replace_in_opf=False): ''' Replace links to files in the container. Will iterate over all files in the container and change the specified links in them.

:param link_map: A mapping of old canonical name to new canonical name. For example: :code:`{'images/old.png': 'images/new.png'}` :param frag_map: A callable that takes two arguments ``(name, anchor)`` and returns a new anchor. This is useful if you need to change the anchors in HTML files. By default, it does nothing. :param replace_in_opf: If False, links are not replaced in the OPF file.

''' for name, media_type in iteritems(container.mime_map): if name == container.opf_name and not replace_in_opf: continue repl = LinkReplacer(name, container, link_map, frag_map) container.replace_links(name, repl)

def replace_ids(container, id_map): ''' Replace all links in the container that pointed to the changed ids.

:param id_map: A mapping of {name:id_map} where each id_map is a mapping of {old_id:new_id} :return: True iff at least one link was changed

''' changed = False for name, media_type in iteritems(container.mime_map): repl = IdReplacer(name, container, id_map) container.replace_links(name, repl) if name == container.opf_name: imap = id_map.get(name, {}) for item in container.opf_xpath('//*[@idref]'): old_id = item.get('idref') if old_id is not None: new_id = imap.get(old_id) if new_id is not None: item.set('idref', new_id) if repl.replaced: changed = True return changed def smarten_punctuation(container, report): from calibre.ebooks.conversion.preprocess import smarten_punctuation smartened = False for path in container.spine_items: name = container.abspath_to_name(path) changed = False with container.open(name, 'r+b') as f: html = container.decode(f.read()) newhtml = smarten_punctuation(html, container.log) if newhtml != html: changed = True report(_('Smartened punctuation in: %s')%name) newhtml = strip_encoding_declarations(newhtml) f.seek(0) f.truncate() f.write(codecs.BOM_UTF8 + newhtml.encode('utf-8')) if changed: # Add an encoding declaration (it will be added automatically when # serialized) root = container.parsed(name) for m in root.xpath('descendant::*[local-name()="meta" and @http-equiv]'): m.getparent().remove(m) container.dirty(name) smartened = True if not smartened: report(_('No punctuation that could be smartened found')) return smartened

[belgeler] def rename_files(container, file_map): ''' Rename files in the container, automatically updating all links to them.

:param file_map: A mapping of old canonical name to new canonical name, for example: :code:`{'text/chapter1.html': 'chapter1.html'}`. ''' overlap = set(file_map).intersection(set(itervalues(file_map))) if overlap: raise ValueError('Circular rename detected. The files %s are both rename targets and destinations' % ', '.join(overlap)) for name, dest in iteritems(file_map): if container.exists(dest): if name != dest and name.lower() == dest.lower(): # A case change on an OS with a case insensitive file-system. continue raise ValueError('Cannot rename {0} to {1} as {1} already exists'.format(name, dest)) if len(tuple(itervalues(file_map))) != len(set(itervalues(file_map))): raise ValueError('Cannot rename, the set of destination files contains duplicates') link_map = {} for current_name, new_name in iteritems(file_map): container.rename(current_name, new_name) if new_name != container.opf_name: # OPF is handled by the container link_map[current_name] = new_name replace_links(container, link_map, replace_in_opf=True)

def replace_file(container, name, path, basename, force_mt=None): dirname, base = name.rpartition('/')[0::2] nname = sanitize_file_name(basename) if dirname: nname = dirname + '/' + nname with open(path, 'rb') as src: if name != nname: count = 0 b, e = nname.rpartition('.')[0::2] while container.exists(nname): count += 1 nname = b + ('_%d.%s' % (count, e)) rename_files(container, {name:nname}) mt = force_mt or container.guess_type(nname) container.mime_map[nname] = mt for itemid, q in iteritems(container.manifest_id_map): if q == nname: for item in container.opf_xpath('//opf:manifest/opf:item[@href and @id="%s"]' % itemid): item.set('media-type', mt) container.dirty(container.opf_name) with container.open(nname, 'wb') as dest: shutil.copyfileobj(src, dest)

def mt_to_category(container, mt): from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES from calibre.ebooks.oeb.polish.utils import OEB_FONTS, guess_type if mt in OEB_DOCS: category = 'text' elif mt in OEB_STYLES: category = 'style' elif mt in OEB_FONTS: category = 'font' elif mt == guess_type('a.opf'): category = 'opf' elif mt == guess_type('a.ncx'): category = 'toc' else: category = mt.partition('/')[0] return category

[belgeler] def get_recommended_folders(container, names): ''' Return the folders that are recommended for the given filenames. The recommendation is based on where the majority of files of the same type are located in the container. If no files of a particular type are present, the recommended folder is assumed to be the folder containing the OPF file. ''' from calibre.ebooks.oeb.polish.utils import guess_type counts = defaultdict(Counter) for name, mt in iteritems(container.mime_map): folder = name.rpartition('/')[0] if '/' in name else '' counts[mt_to_category(container, mt)][folder] += 1 try: opf_folder = counts['opf'].most_common(1)[0][0] except KeyError: opf_folder = ''

recommendations = {category:counter.most_common(1)[0][0] for category, counter in iteritems(counts)} return {n:recommendations.get(mt_to_category(container, guess_type(os.path.basename(n))), opf_folder) for n in names}

def normalize_case(container, val):

def safe_listdir(x): try: return os.listdir(x) except OSError: return ()

parts = val.split('/') ans = [] for i in range(len(parts)): q = '/'.join(parts[:i+1]) x = container.name_to_abspath(q) xl = parts[i].lower() candidates = [c for c in safe_listdir(os.path.dirname(x)) if c != parts[i] and c.lower() == xl] ans.append(candidates[0] if candidates else parts[i]) return '/'.join(ans)

def rationalize_folders(container, folder_type_map): all_names = set(container.mime_map) new_names = set() name_map = {} for key in tuple(folder_type_map): val = folder_type_map[key] folder_type_map[key] = normalize_case(container, val) for name in all_names: if name.startswith('META-INF/'): continue category = mt_to_category(container, container.mime_map[name]) folder = folder_type_map.get(category, None) if folder is not None: bn = posixpath.basename(name) new_name = posixpath.join(folder, bn) if new_name != name: c = 0 while new_name in all_names or new_name in new_names: c += 1 n, ext = bn.rpartition('.')[0::2] new_name = posixpath.join(folder, '%s_%d.%s' % (n, c, ext)) name_map[name] = new_name new_names.add(new_name) return name_map def remove_links_in_sheet(href_to_name, sheet, predicate): import_rules_to_remove = [] changed = False for i, r in enumerate(sheet): if r.type == r.IMPORT_RULE: name = href_to_name(r.href) if predicate(name, r.href, None): import_rules_to_remove.append(i) for i in sorted(import_rules_to_remove, reverse=True): sheet.deleteRule(i) changed = True for dec in iter_declarations(sheet): changed = remove_links_in_declaration(href_to_name, dec, predicate) or changed return changed def remove_links_in_declaration(href_to_name, style, predicate): def check_pval(v): if v.type == v.URI: name = href_to_name(v.uri) return predicate(name, v.uri, None) return False changed = False for p in tuple(style.getProperties(all=True)): changed = remove_property_value(p, check_pval) or changed return changed def remove_links_to(container, predicate): ''' predicate must be a function that takes the arguments (name, href, fragment=None) and returns True iff the link should be removed ''' from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, XHTML, XPath, iterlinks stylepath = XPath('//h:style') styleattrpath = XPath('//*[@style]') changed = set() for name, mt in iteritems(container.mime_map): removed = False if mt in OEB_DOCS: root = container.parsed(name) for el, attr, href, pos in iterlinks(root, find_links_in_css=False): hname = container.href_to_name(href, name) frag = href.partition('#')[-1] if predicate(hname, href, frag): if attr is None: el.text = None else: if el.tag == XHTML('link') or el.tag == XHTML('img'): extract(el) else: del el.attrib[attr] removed = True for tag in stylepath(root): if tag.text and (tag.get('type') or 'text/css').lower() == 'text/css': sheet = container.parse_css(tag.text) if remove_links_in_sheet(partial(container.href_to_name, base=name), sheet, predicate): tag.text = css_text(sheet) removed = True for tag in styleattrpath(root): style = tag.get('style') if style: style = container.parse_css(style, is_declaration=True) if remove_links_in_declaration(partial(container.href_to_name, base=name), style, predicate): removed = True tag.set('style', css_text(style)) elif mt in OEB_STYLES: removed = remove_links_in_sheet(partial(container.href_to_name, base=name), container.parsed(name), predicate) if removed: changed.add(name) for i in changed: container.dirty(i) return changed def get_spine_order_for_all_files(container): linear_names, non_linear_names = [], [] for name, is_linear in container.spine_names: (linear_names if is_linear else non_linear_names).append(name) all_names = linear_names + non_linear_names spine_names = frozenset(all_names) ans = {} for spine_pos, name in enumerate(all_names): ans.setdefault(name, (spine_pos, -1)) for i, href in enumerate(container.iterlinks(name, get_line_numbers=False)): lname = container.href_to_name(href, name) if lname not in spine_names: ans.setdefault(lname, (spine_pos, i)) return ans

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.split

calibre.ebooks.oeb.polish.split öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'

import copy import os import re from calibre.ebooks.oeb.base import OEB_DOCS, OPF, XHTML, XPNSMAP, XPath, barename from calibre.ebooks.oeb.polish.errors import MalformedMarkup from calibre.ebooks.oeb.polish.replace import LinkRebaser from calibre.ebooks.oeb.polish.toc import node_from_loc from polyglot.builtins import iteritems, string_or_bytes from polyglot.urllib import urlparse class AbortError(ValueError): pass def in_table(node): while node is not None: if node.tag.endswith('}table'): return True node = node.getparent() return False def adjust_split_point(split_point, log): ''' Move the split point up its ancestor chain if it has no content before it. This handles the common case: <div id="chapter1"><h2>Chapter 1</h2>...</div> with a page break on the h2. ''' sp = split_point while True: parent = sp.getparent() if ( parent is None or barename(parent.tag) in {'body', 'html'} or (parent.text and parent.text.strip()) or parent.index(sp) > 0 ): break sp = parent if sp is not split_point: log.debug('Adjusted split point to ancestor')

return sp def get_body(root): return root.find('h:body', namespaces=XPNSMAP)

def do_split(split_point, log, before=True): ''' Split tree into a *before* and an *after* tree at ``split_point``.

:param split_point: The Element at which to split :param before: If True tree is split before split_point, otherwise after split_point :return: before_tree, after_tree ''' if before: # We cannot adjust for after since moving an after split point to a # parent will cause breakage if the parent contains any content # after the original split point split_point = adjust_split_point(split_point, log) tree = split_point.getroottree() path = tree.getpath(split_point)

tree, tree2 = copy.deepcopy(tree), copy.deepcopy(tree) root, root2 = tree.getroot(), tree2.getroot() body, body2 = map(get_body, (root, root2)) split_point = root.xpath(path)[0] split_point2 = root2.xpath(path)[0]

def nix_element(elem, top=True): # Remove elem unless top is False in which case replace elem by its # children parent = elem.getparent() if top: parent.remove(elem) else: index = parent.index(elem) parent[index:index+1] = list(elem.iterchildren())

# Tree 1 hit_split_point = False keep_descendants = False split_point_descendants = frozenset(split_point.iterdescendants()) for elem in tuple(body.iterdescendants()): if elem is split_point: hit_split_point = True if before: nix_element(elem) else: # We want to keep the descendants of the split point in # Tree 1 keep_descendants = True # We want the split point element, but not its tail elem.tail = '\n'

continue if hit_split_point: if keep_descendants: if elem in split_point_descendants: # elem is a descendant keep it continue else: # We are out of split_point, so prevent further set # lookups of split_point_descendants keep_descendants = False nix_element(elem)

# Tree 2 ancestors = frozenset(XPath('ancestor::*')(split_point2)) for elem in tuple(body2.iterdescendants()): if elem is split_point2: if not before: # Keep the split point element's tail, if it contains non-whitespace # text tail = elem.tail if tail and not tail.isspace(): parent = elem.getparent() idx = parent.index(elem) if idx == 0: parent.text = (parent.text or '') + tail else: sib = parent[idx-1] sib.tail = (sib.tail or '') + tail # Remove the element itself nix_element(elem) break if elem in ancestors: # We have to preserve the ancestors as they could have CSS # styles that are inherited/applicable, like font or # width. So we only remove the text, if any. elem.text = '\n' else: nix_element(elem, top=False)

body2.text = '\n'

return tree, tree2 class SplitLinkReplacer:

def __init__(self, base, bottom_anchors, top_name, bottom_name, container): self.bottom_anchors, self.bottom_name = bottom_anchors, bottom_name self.container, self.top_name = container, top_name self.base = base self.replaced = False def __call__(self, url): if url and url.startswith('#'): return url name = self.container.href_to_name(url, self.base) if name != self.top_name: return url purl = urlparse(url) if purl.fragment and purl.fragment in self.bottom_anchors: url = self.container.name_to_href(self.bottom_name, self.base) + '#' + purl.fragment self.replaced = True return url

[belgeler] def split(container, name, loc_or_xpath, before=True, totals=None): ''' Split the file specified by name at the position specified by loc_or_xpath. Splitting automatically migrates all links and references to the affected files.

:param loc_or_xpath: Should be an XPath expression such as //h:div[@id="split_here"]. Can also be a *loc* which is used internally to implement splitting in the preview panel. :param before: If True the split occurs before the identified element otherwise after it. :param totals: Used internally '''

root = container.parsed(name) if isinstance(loc_or_xpath, str): split_point = root.xpath(loc_or_xpath)[0] else: try: split_point = node_from_loc(root, loc_or_xpath, totals=totals) except MalformedMarkup: # The webkit HTML parser and the container parser have yielded # different node counts, this can happen if the file is valid XML # but contains constructs like nested <p> tags. So force parse it # with the HTML 5 parser and try again. raw = container.raw_data(name) root = container.parse_xhtml(raw, fname=name, force_html5_parse=True) try: split_point = node_from_loc(root, loc_or_xpath, totals=totals) except MalformedMarkup: raise MalformedMarkup(_('The file %s has malformed markup. Try running the Fix HTML tool' ' before splitting') % name) container.replace(name, root) if in_table(split_point): raise AbortError('Cannot split inside tables') if split_point.tag.endswith('}body'): raise AbortError('Cannot split on the <body> tag') tree1, tree2 = do_split(split_point, container.log, before=before) root1, root2 = tree1.getroot(), tree2.getroot() anchors_in_top = frozenset(root1.xpath('//*/@id')) | frozenset(root1.xpath('//*/@name')) | {''} anchors_in_bottom = frozenset(root2.xpath('//*/@id')) | frozenset(root2.xpath('//*/@name')) base, ext = name.rpartition('.')[0::2] base = re.sub(r'_split\d+$', '', base) nname, s = None, 0 while not nname or container.exists(nname): s += 1 nname = '%s_split%d.%s' % (base, s, ext) manifest_item = container.generate_item(nname, media_type=container.mime_map[name]) bottom_name = container.href_to_name(manifest_item.get('href'), container.opf_name)

# Fix links in the split trees for r in (root1, root2): for a in r.xpath('//*[@href]'): url = a.get('href') if url.startswith('#'): fname = name else: fname = container.href_to_name(url, name) if fname == name: purl = urlparse(url) if purl.fragment in anchors_in_top: if r is root2: a.set('href', f'{container.name_to_href(name, bottom_name)}#{purl.fragment}') else: a.set('href', '#' + purl.fragment) elif purl.fragment in anchors_in_bottom: if r is root1: a.set('href', f'{container.name_to_href(bottom_name, name)}#{purl.fragment}') else: a.set('href', '#' + purl.fragment)

# Fix all links in the container that point to anchors in the bottom tree for fname, media_type in iteritems(container.mime_map): if fname not in {name, bottom_name}: repl = SplitLinkReplacer(fname, anchors_in_bottom, name, bottom_name, container) container.replace_links(fname, repl)

container.replace(name, root1) container.replace(bottom_name, root2)

spine = container.opf_xpath('//opf:spine')[0] for spine_item, spine_name, linear in container.spine_iter: if spine_name == name: break index = spine.index(spine_item) + 1 si = spine.makeelement(OPF('itemref'), idref=manifest_item.get('id')) if not linear: si.set('linear', 'no') container.insert_into_xml(spine, si, index=index) container.dirty(container.opf_name) return bottom_name

[belgeler] def multisplit(container, name, xpath, before=True): ''' Split the specified file at multiple locations (all tags that match the specified XPath expression). See also: :func:`split`. Splitting automatically migrates all links and references to the affected files.

:param before: If True the splits occur before the identified element otherwise after it. ''' root = container.parsed(name) nodes = root.xpath(xpath, namespaces=XPNSMAP) if not nodes: raise AbortError(_('The expression %s did not match any nodes') % xpath) for split_point in nodes: if in_table(split_point): raise AbortError('Cannot split inside tables') if split_point.tag.endswith('}body'): raise AbortError('Cannot split on the <body> tag')

for i, tag in enumerate(nodes): tag.set('calibre-split-point', str(i))

current = name all_names = [name] for i in range(len(nodes)): current = split(container, current, '//*[@calibre-split-point="%d"]' % i, before=before) all_names.append(current)

for x in all_names: for tag in container.parsed(x).xpath('//*[@calibre-split-point]'): tag.attrib.pop('calibre-split-point') container.dirty(x)

return all_names[1:]

class MergeLinkReplacer:

def __init__(self, base, anchor_map, master, container): self.container, self.anchor_map = container, anchor_map self.master = master self.base = base self.replaced = False def __call__(self, url): if url and url.startswith('#'): return url name = self.container.href_to_name(url, self.base) amap = self.anchor_map.get(name, None) if amap is None: return url purl = urlparse(url) frag = purl.fragment or '' frag = amap.get(frag, frag) url = self.container.name_to_href(self.master, self.base) + '#' + frag self.replaced = True return url def add_text(body, text): if len(body) > 0: body[-1].tail = (body[-1].tail or '') + text else: body.text = (body.text or '') + text def all_anchors(root): return set(root.xpath('//*/@id')) | set(root.xpath('//*/@name'))

def all_stylesheets(container, name): for link in XPath('//h:head/h:link[@href]')(container.parsed(name)): name = container.href_to_name(link.get('href'), name) typ = link.get('type', 'text/css') if typ == 'text/css': yield name def unique_anchor(seen_anchors, current): c = 0 ans = current while ans in seen_anchors: c += 1 ans = '%s_%d' % (current, c) return ans def remove_name_attributes(root): # Remove all name attributes, replacing them with id attributes for elem in root.xpath('//*[@id and @name]'): del elem.attrib['name'] for elem in root.xpath('//*[@name]'): elem.set('id', elem.attrib.pop('name'))

def merge_html(container, names, master, insert_page_breaks=False): p = container.parsed root = p(master)

# Ensure master has a <head> head = root.find('h:head', namespaces=XPNSMAP) if head is None: head = root.makeelement(XHTML('head')) container.insert_into_xml(root, head, 0)

seen_anchors = all_anchors(root) seen_stylesheets = set(all_stylesheets(container, master)) master_body = p(master).findall('h:body', namespaces=XPNSMAP)[-1] master_base = os.path.dirname(master) anchor_map = {n:{} for n in names if n != master} first_anchor_map = {}

for name in names: if name == master: continue # Insert new stylesheets into master for sheet in all_stylesheets(container, name): if sheet not in seen_stylesheets: seen_stylesheets.add(sheet) link = head.makeelement(XHTML('link'), rel='stylesheet', type='text/css', href=container.name_to_href(sheet, master)) container.insert_into_xml(head, link)

# Rebase links if master is in a different directory if os.path.dirname(name) != master_base: container.replace_links(name, LinkRebaser(container, name, master))

root = p(name) children = [] for body in p(name).findall('h:body', namespaces=XPNSMAP): children.append(body.text if body.text and body.text.strip() else '\n\n') children.extend(body)

first_child = '' for first_child in children: if not isinstance(first_child, string_or_bytes): break if isinstance(first_child, string_or_bytes): # body contained only text, no tags first_child = body.makeelement(XHTML('p')) first_child.text, children[0] = children[0], first_child amap = anchor_map[name] remove_name_attributes(root)

for elem in root.xpath('//*[@id]'): val = elem.get('id') if not val: continue if val in seen_anchors: nval = unique_anchor(seen_anchors, val) elem.set('id', nval) amap[val] = nval else: seen_anchors.add(val)

if 'id' not in first_child.attrib: first_child.set('id', unique_anchor(seen_anchors, 'top')) seen_anchors.add(first_child.get('id')) first_anchor_map[name] = first_child.get('id')

if insert_page_breaks: first_child.set('style', first_child.get('style', '') + '; page-break-before: always')

amap[''] = first_child.get('id')

# Fix links that point to local changed anchors for a in XPath('//h:a[starts-with(@href, "#")]')(root): q = a.get('href')[1:] if q in amap: a.set('href', '#' + amap[q])

for child in children: if isinstance(child, string_or_bytes): add_text(master_body, child) else: master_body.append(copy.deepcopy(child))

container.remove_item(name, remove_from_guide=False)

# Fix all links in the container that point to merged files for fname, media_type in iteritems(container.mime_map): repl = MergeLinkReplacer(fname, anchor_map, master, container) container.replace_links(fname, repl)

return first_anchor_map def merge_css(container, names, master): p = container.parsed msheet = p(master) master_base = os.path.dirname(master) merged = set()

for name in names: if name == master: continue # Rebase links if master is in a different directory if os.path.dirname(name) != master_base: container.replace_links(name, LinkRebaser(container, name, master))

sheet = p(name)

# Remove charset rules cr = [r for r in sheet.cssRules if r.type == r.CHARSET_RULE] [sheet.deleteRule(sheet.cssRules.index(r)) for r in cr] for rule in sheet.cssRules: msheet.add(rule)

container.remove_item(name) merged.add(name)

# Remove links to merged stylesheets in the html files, replacing with a # link to the master sheet for name, mt in iteritems(container.mime_map): if mt in OEB_DOCS: removed = False root = p(name) for link in XPath('//h:link[@href]')(root): q = container.href_to_name(link.get('href'), name) if q in merged: container.remove_from_xml(link) removed = True if removed: container.dirty(name) if removed and master not in set(all_stylesheets(container, name)): head = root.find('h:head', namespaces=XPNSMAP) if head is not None: link = head.makeelement(XHTML('link'), type='text/css', rel='stylesheet', href=container.name_to_href(master, name)) container.insert_into_xml(head, link)

[belgeler] def merge(container, category, names, master): ''' Merge the specified files into a single file, automatically migrating all links and references to the affected files. The file must all either be HTML or CSS files.

:param category: Must be either ``'text'`` for HTML files or ``'styles'`` for CSS files :param names: The list of files to be merged :param master: Which of the merged files is the *master* file, that is, the file that will remain after merging. ''' if category not in {'text', 'styles'}: raise AbortError('Cannot merge files of type: %s' % category) if len(names) < 2: raise AbortError('Must specify at least two files to be merged') if master not in names: raise AbortError('The master file (%s) must be one of the files being merged' % master)

if category == 'text': merge_html(container, names, master) elif category == 'styles': merge_css(container, names, master)

container.dirty(master)

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.ebooks.oeb.polish.toc

calibre.ebooks.oeb.polish.toc öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import re from collections import Counter, OrderedDict from functools import partial from operator import itemgetter from lxml import etree from lxml.builder import ElementMaker from calibre import __version__ from calibre.ebooks.oeb.base import EPUB_NS, NCX, NCX_NS, OEB_DOCS, XHTML, XHTML_NS, XML, XML_NS, XPath, serialize, uuid_id, xml2text from calibre.ebooks.oeb.polish.errors import MalformedMarkup from calibre.ebooks.oeb.polish.opf import get_book_language, set_guide_item from calibre.ebooks.oeb.polish.pretty import pretty_html_tree, pretty_xml_tree from calibre.ebooks.oeb.polish.utils import extract, guess_type from calibre.translations.dynamic import translate from calibre.utils.localization import canonicalize_lang, get_lang, lang_as_iso639_1 from calibre.utils.resources import get_path as P from polyglot.builtins import iteritems from polyglot.urllib import urlparse ns = etree.FunctionNamespace('calibre_xpath_extensions') ns.prefix = 'calibre' ns['lower-case'] = lambda c, x: x.lower() if hasattr(x, 'lower') else x class TOC:

toc_title = None def __init__(self, title=None, dest=None, frag=None): self.title, self.dest, self.frag = title, dest, frag self.dest_exists = self.dest_error = None if self.title: self.title = self.title.strip() self.parent = None self.children = [] self.page_list = []

def add(self, title, dest, frag=None): c = TOC(title, dest, frag) self.children.append(c) c.parent = self return c def remove(self, child): self.children.remove(child) child.parent = None def remove_from_parent(self): if self.parent is None: return idx = self.parent.children.index(self) for child in reversed(self.children): child.parent = self.parent self.parent.children.insert(idx, child) self.parent.children.remove(self) self.parent = None def __iter__(self): yield from self.children def __len__(self): return len(self.children)

def iterdescendants(self, level=None): gc_level = None if level is None else level + 1 for child in self: if level is None: yield child else: yield level, child yield from child.iterdescendants(level=gc_level)

def remove_duplicates(self, only_text=True): seen = set() remove = [] for child in self: key = child.title if only_text else (child.title, child.dest, (child.frag or None)) if key in seen: remove.append(child) else: seen.add(key) child.remove_duplicates() for child in remove: self.remove(child)

@property def depth(self): """The maximum depth of the navigation tree rooted at this node.""" try: return max(node.depth for node in self) + 1 except ValueError: return 1

@property def last_child(self): return self.children[-1] if self.children else None def get_lines(self, lvl=0): frag = ('#'+self.frag) if self.frag else '' ans = [('\t'*lvl) + 'TOC: %s --> %s%s'%(self.title, self.dest, frag)] for child in self: ans.extend(child.get_lines(lvl+1)) return ans def __str__(self): return '\n'.join(self.get_lines())

def to_dict(self, node_counter=None): ans = { 'title':self.title, 'dest':self.dest, 'frag':self.frag, 'children':[c.to_dict(node_counter) for c in self.children] } if self.dest_exists is not None: ans['dest_exists'] = self.dest_exists if self.dest_error is not None: ans['dest_error'] = self.dest_error if node_counter is not None: ans['id'] = next(node_counter) return ans

@property def as_dict(self): return self.to_dict()

def child_xpath(tag, name): return tag.xpath('./*[calibre:lower-case(local-name()) = "%s"]'%name)

def add_from_navpoint(container, navpoint, parent, ncx_name): dest = frag = text = None nl = child_xpath(navpoint, 'navlabel') if nl: nl = nl[0] text = '' for txt in child_xpath(nl, 'text'): text += etree.tostring(txt, method='text', encoding='unicode', with_tail=False) content = child_xpath(navpoint, 'content') if content: content = content[0] href = content.get('src', None) if href: dest = container.href_to_name(href, base=ncx_name) frag = urlparse(href).fragment or None return parent.add(text or None, dest or None, frag or None)

def process_ncx_node(container, node, toc_parent, ncx_name): for navpoint in node.xpath('./*[calibre:lower-case(local-name()) = "navpoint"]'): child = add_from_navpoint(container, navpoint, toc_parent, ncx_name) if child is not None: process_ncx_node(container, navpoint, child, ncx_name)

def parse_ncx(container, ncx_name): root = container.parsed(ncx_name) toc_root = TOC() navmaps = root.xpath('//*[calibre:lower-case(local-name()) = "navmap"]') if navmaps: process_ncx_node(container, navmaps[0], toc_root, ncx_name) toc_root.lang = toc_root.uid = None for attr, val in iteritems(root.attrib): if attr.endswith('lang'): toc_root.lang = str(val) break for uid in root.xpath('//*[calibre:lower-case(local-name()) = "meta" and @name="dtb:uid"]/@content'): if uid: toc_root.uid = str(uid) break for pl in root.xpath('//*[calibre:lower-case(local-name()) = "pagelist"]'): for pt in pl.xpath('descendant::*[calibre:lower-case(local-name()) = "pagetarget"]'): pagenum = pt.get('value') if pagenum: href = pt.xpath('descendant::*[calibre:lower-case(local-name()) = "content"]/@src') if href: dest = container.href_to_name(href[0], base=ncx_name) frag = urlparse(href[0]).fragment or None toc_root.page_list.append({'dest': dest, 'pagenum': pagenum, 'frag': frag}) return toc_root def add_from_li(container, li, parent, nav_name): dest = frag = text = None for x in li.iterchildren(XHTML('a'), XHTML('span')): text = etree.tostring(x, method='text', encoding='unicode', with_tail=False).strip() or ' '.join(x.xpath('descendant-or-self::*/@title')).strip() href = x.get('href') if href: dest = nav_name if href.startswith('#') else container.href_to_name(href, base=nav_name) frag = urlparse(href).fragment or None break return parent.add(text or None, dest or None, frag or None)

def first_child(parent, tagname): try: return next(parent.iterchildren(tagname)) except StopIteration: return None def process_nav_node(container, node, toc_parent, nav_name): for li in node.iterchildren(XHTML('li')): child = add_from_li(container, li, toc_parent, nav_name) ol = first_child(li, XHTML('ol')) if child is not None and ol is not None: process_nav_node(container, ol, child, nav_name)

def parse_nav(container, nav_name): root = container.parsed(nav_name) toc_root = TOC() toc_root.lang = toc_root.uid = None et = '{%s}type' % EPUB_NS for nav in root.iterdescendants(XHTML('nav')): if nav.get(et) == 'toc': ol = first_child(nav, XHTML('ol')) if ol is not None: process_nav_node(container, ol, toc_root, nav_name) for h in nav.iterchildren(*map(XHTML, 'h1 h2 h3 h4 h5 h6'.split())): text = etree.tostring(h, method='text', encoding='unicode', with_tail=False) or h.get('title') if text: toc_root.toc_title = text break break return toc_root def verify_toc_destinations(container, toc): anchor_map = {} anchor_xpath = XPath('//*/@id|//h:a/@name') for item in toc.iterdescendants(): name = item.dest if not name: item.dest_exists = False item.dest_error = _('No file named %s exists')%name continue try: root = container.parsed(name) except KeyError: item.dest_exists = False item.dest_error = _('No file named %s exists')%name continue if not hasattr(root, 'xpath'): item.dest_exists = False item.dest_error = _('No HTML file named %s exists')%name continue if not item.frag: item.dest_exists = True continue if name not in anchor_map: anchor_map[name] = frozenset(anchor_xpath(root)) item.dest_exists = item.frag in anchor_map[name] if not item.dest_exists: item.dest_error = _( 'The anchor %(a)s does not exist in file %(f)s')%dict( a=item.frag, f=name)

def find_existing_ncx_toc(container): toc = container.opf_xpath('//opf:spine/@toc') if toc: toc = container.manifest_id_map.get(toc[0], None) if not toc: ncx = guess_type('a.ncx') toc = container.manifest_type_map.get(ncx, [None])[0] return toc or None def find_existing_nav_toc(container): for name in container.manifest_items_with_property('nav'): return name def mark_as_nav(container, name): if container.opf_version_parsed.major > 2: container.apply_unique_properties(name, 'nav')

def get_x_toc(container, find_toc, parse_toc, verify_destinations=True): def empty_toc(): ans = TOC() ans.lang = ans.uid = None return ans toc = find_toc(container) ans = empty_toc() if toc is None or not container.has_name(toc) else parse_toc(container, toc) ans.toc_file_name = toc if toc and container.has_name(toc) else None if verify_destinations: verify_toc_destinations(container, ans) return ans def get_toc(container, verify_destinations=True): ver = container.opf_version_parsed if ver.major < 3: return get_x_toc(container, find_existing_ncx_toc, parse_ncx, verify_destinations=verify_destinations) else: ans = get_x_toc(container, find_existing_nav_toc, parse_nav, verify_destinations=verify_destinations) if len(ans) == 0: ans = get_x_toc(container, find_existing_ncx_toc, parse_ncx, verify_destinations=verify_destinations) return ans def get_guide_landmarks(container): for ref in container.opf_xpath('./opf:guide/opf:reference'): href, title, rtype = ref.get('href'), ref.get('title'), ref.get('type') href, frag = href.partition('#')[::2] name = container.href_to_name(href, container.opf_name) if container.has_name(name): yield {'dest':name, 'frag':frag, 'title':title or '', 'type':rtype or ''}

def get_nav_landmarks(container): nav = find_existing_nav_toc(container) if nav and container.has_name(nav): root = container.parsed(nav) et = '{%s}type' % EPUB_NS for elem in root.iterdescendants(XHTML('nav')): if elem.get(et) == 'landmarks': for li in elem.iterdescendants(XHTML('li')): for a in li.iterdescendants(XHTML('a')): href, rtype = a.get('href'), a.get(et) if href: title = etree.tostring(a, method='text', encoding='unicode', with_tail=False).strip() href, frag = href.partition('#')[::2] name = container.href_to_name(href, nav) if container.has_name(name): yield {'dest':name, 'frag':frag, 'title':title or '', 'type':rtype or ''} break def get_landmarks(container): ver = container.opf_version_parsed if ver.major < 3: return list(get_guide_landmarks(container)) ans = list(get_nav_landmarks(container)) if len(ans) == 0: ans = list(get_guide_landmarks(container)) return ans def ensure_id(elem, all_ids): elem_id = elem.get('id') if elem_id: return False, elem_id if elem.tag == XHTML('a'): anchor = elem.get('name', None) if anchor: elem.set('id', anchor) return False, anchor c = 0 while True: c += 1 q = f'toc_{c}' if q not in all_ids: elem.set('id', q) all_ids.add(q) break return True, elem.get('id')

def elem_to_toc_text(elem, prefer_title=False): text = xml2text(elem).strip() if prefer_title: text = elem.get('title', '').strip() or text if not text: text = elem.get('title', '') if not text: text = elem.get('alt', '') text = re.sub(r'\s+', ' ', text.strip()) text = text[:1000].strip() if not text: text = _('(Untitled)') return text def item_at_top(elem): try: body = XPath('//h:body')(elem.getroottree().getroot())[0] except (TypeError, IndexError, KeyError, AttributeError): return False tree = body.getroottree() path = tree.getpath(elem) for el in body.iterdescendants(etree.Element): epath = tree.getpath(el) if epath == path: break try: if el.tag.endswith('}img') or (el.text and el.text.strip()): return False except: return False if not path.startswith(epath): # Only check tail of non-parent elements if el.tail and el.tail.strip(): return False return True

[belgeler] def from_xpaths(container, xpaths, prefer_title=False): ''' Generate a Table of Contents from a list of XPath expressions. Each expression in the list corresponds to a level of the generate ToC. For example: :code:`['//h:h1', '//h:h2', '//h:h3']` will generate a three level Table of Contents from the ``<h1>``, ``<h2>`` and ``<h3>`` tags. ''' tocroot = TOC() xpaths = [XPath(xp) for xp in xpaths]

# Find those levels that have no elements in all spine items maps = OrderedDict() empty_levels = {i+1 for i, xp in enumerate(xpaths)} for spinepath in container.spine_items: name = container.abspath_to_name(spinepath) root = container.parsed(name) level_item_map = maps[name] = {i+1:frozenset(xp(root)) for i, xp in enumerate(xpaths)} for lvl, elems in iteritems(level_item_map): if elems: empty_levels.discard(lvl) # Remove empty levels from all level_maps if empty_levels: for name, lmap in tuple(iteritems(maps)): lmap = {lvl:items for lvl, items in iteritems(lmap) if lvl not in empty_levels} lmap = sorted(iteritems(lmap), key=itemgetter(0)) lmap = {i+1:items for i, (l, items) in enumerate(lmap)} maps[name] = lmap node_level_map = {tocroot: 0}

def parent_for_level(child_level): limit = child_level - 1 def process_node(node): child = node.last_child if child is None: return node lvl = node_level_map[child] return node if lvl > limit else child if lvl == limit else process_node(child)

return process_node(tocroot)

for name, level_item_map in iteritems(maps): root = container.parsed(name) item_level_map = {e:i for i, elems in iteritems(level_item_map) for e in elems} item_dirtied = False all_ids = set(root.xpath('//*/@id'))

for item in root.iterdescendants(etree.Element): lvl = item_level_map.get(item, None) if lvl is None: continue text = elem_to_toc_text(item, prefer_title) parent = parent_for_level(lvl) if item_at_top(item): dirtied, elem_id = False, None else: dirtied, elem_id = ensure_id(item, all_ids) item_dirtied = dirtied or item_dirtied toc = parent.add(text, name, elem_id) node_level_map[toc] = lvl toc.dest_exists = True if item_dirtied: container.commit_item(name, keep_parsed=True)

return tocroot[belgeler] def from_links(container): ''' Generate a Table of Contents from links in the book. ''' toc = TOC() link_path = XPath('//h:a[@href]') seen_titles, seen_dests = set(), set() for name, is_linear in container.spine_names: root = container.parsed(name) for a in link_path(root): href = a.get('href') if not href or not href.strip(): continue frag = None if href.startswith('#'): dest = name frag = href[1:] else: href, _, frag = href.partition('#') dest = container.href_to_name(href, base=name) frag = frag or None if (dest, frag) in seen_dests: continue seen_dests.add((dest, frag)) text = elem_to_toc_text(a) if text in seen_titles: continue seen_titles.add(text) toc.add(text, dest, frag=frag) verify_toc_destinations(container, toc) for child in toc: if not child.dest_exists: toc.remove(child) return tocdef find_text(node): LIMIT = 200 pat = re.compile(r'\s+') for child in node: if isinstance(child, etree._Element): text = xml2text(child).strip() text = pat.sub(' ', text) if len(text) < 1: continue if len(text) > LIMIT: # Look for less text in a child of this node, recursively ntext = find_text(child) return ntext or (text[:LIMIT] + '...') else: return text

[belgeler] def from_files(container): ''' Generate a Table of Contents from files in the book. ''' toc = TOC() for i, spinepath in enumerate(container.spine_items): name = container.abspath_to_name(spinepath) root = container.parsed(name) body = XPath('//h:body')(root) if not body: continue text = find_text(body[0]) if not text: text = name.rpartition('/')[-1] if i == 0 and text.rpartition('.')[0].lower() in {'titlepage', 'cover'}: text = _('Cover') toc.add(text, name) return toc
def node_from_loc(root, locs, totals=None): node = root.xpath('//*[local-name()="body"]')[0] for i, loc in enumerate(locs): children = tuple(node.iterchildren(etree.Element)) if totals is not None and totals[i] != len(children): raise MalformedMarkup() node = children[loc] return node def add_id(container, name, loc, totals=None): root = container.parsed(name) try: node = node_from_loc(root, loc, totals=totals) except MalformedMarkup: # The webkit HTML parser and the container parser have yielded # different node counts, this can happen if the file is valid XML # but contains constructs like nested <p> tags. So force parse it # with the HTML 5 parser and try again. raw = container.raw_data(name) root = container.parse_xhtml(raw, fname=name, force_html5_parse=True) try: node = node_from_loc(root, loc, totals=totals) except MalformedMarkup: raise MalformedMarkup(_('The file %s has malformed markup. Try running the Fix HTML tool' ' before editing.') % name) container.replace(name, root)

if not node.get('id'): ensure_id(node, set(root.xpath('//*/@id'))) container.commit_item(name, keep_parsed=True) return node.get('id')

def create_ncx(toc, to_href, btitle, lang, uid): lang = lang.replace('_', '-') ncx = etree.Element(NCX('ncx'), attrib={'version': '2005-1', XML('lang'): lang}, nsmap={None: NCX_NS}) head = etree.SubElement(ncx, NCX('head')) etree.SubElement(head, NCX('meta'), name='dtb:uid', content=str(uid)) etree.SubElement(head, NCX('meta'), name='dtb:depth', content=str(toc.depth)) generator = ''.join(['calibre (', __version__, ')']) etree.SubElement(head, NCX('meta'), name='dtb:generator', content=generator) etree.SubElement(head, NCX('meta'), name='dtb:totalPageCount', content='0') etree.SubElement(head, NCX('meta'), name='dtb:maxPageNumber', content='0') title = etree.SubElement(ncx, NCX('docTitle')) text = etree.SubElement(title, NCX('text')) text.text = btitle navmap = etree.SubElement(ncx, NCX('navMap')) spat = re.compile(r'\s+')

play_order = Counter()

def process_node(xml_parent, toc_parent): for child in toc_parent: play_order['c'] += 1 point = etree.SubElement(xml_parent, NCX('navPoint'), id='num_%d' % play_order['c'], playOrder=str(play_order['c'])) label = etree.SubElement(point, NCX('navLabel')) title = child.title if title: title = spat.sub(' ', title) etree.SubElement(label, NCX('text')).text = title if child.dest: href = to_href(child.dest) if child.frag: href += '#'+child.frag etree.SubElement(point, NCX('content'), src=href) process_node(point, child)

process_node(navmap, toc) return ncx def commit_ncx_toc(container, toc, lang=None, uid=None): tocname = find_existing_ncx_toc(container) if tocname is None: item = container.generate_item('toc.ncx', id_prefix='toc') tocname = container.href_to_name(item.get('href'), base=container.opf_name) ncx_id = item.get('id') [s.set('toc', ncx_id) for s in container.opf_xpath('//opf:spine')] if not lang: lang = get_lang() for l in container.opf_xpath('//dc:language'): l = canonicalize_lang(xml2text(l).strip()) if l: lang = l lang = lang_as_iso639_1(l) or l break lang = lang_as_iso639_1(lang) or lang if not uid: uid = uuid_id() eid = container.opf.get('unique-identifier', None) if eid: m = container.opf_xpath('//*[@id="%s"]'%eid) if m: uid = xml2text(m[0])

title = _('Table of Contents') m = container.opf_xpath('//dc:title') if m: x = xml2text(m[0]).strip() title = x or title to_href = partial(container.name_to_href, base=tocname) root = create_ncx(toc, to_href, title, lang, uid) container.replace(tocname, root) container.pretty_print.add(tocname)

def ensure_single_nav_of_type(root, ntype='toc'): et = '{%s}type' % EPUB_NS navs = [n for n in root.iterdescendants(XHTML('nav')) if n.get(et) == ntype] for x in navs[1:]: extract(x) if navs: nav = navs[0] tail = nav.tail attrib = dict(nav.attrib) nav.clear() nav.attrib.update(attrib) nav.tail = tail else: nav = root.makeelement(XHTML('nav')) first_child(root, XHTML('body')).append(nav) nav.set('{%s}type' % EPUB_NS, ntype) return nav def ensure_container_has_nav(container, lang=None, previous_nav=None): tocname = find_existing_nav_toc(container) if previous_nav is not None: nav_name = container.href_to_name(previous_nav[0]) if nav_name and container.exists(nav_name): tocname = nav_name container.apply_unique_properties(tocname, 'nav') if tocname is None: item = container.generate_item('nav.xhtml', id_prefix='nav') item.set('properties', 'nav') tocname = container.href_to_name(item.get('href'), base=container.opf_name) if previous_nav is not None: root = previous_nav[1] else: root = container.parse_xhtml(P('templates/new_nav.html', data=True).decode('utf-8')) container.replace(tocname, root) else: root = container.parsed(tocname) if lang: lang = lang_as_iso639_1(lang) or lang root.set('lang', lang) root.set('{%s}lang' % XML_NS, lang) return tocname, root def collapse_li(parent): for li in parent.iterdescendants(XHTML('li')): if len(li) == 1: li.text = None li[0].tail = None def create_nav_li(container, ol, entry, tocname): li = ol.makeelement(XHTML('li')) ol.append(li) a = li.makeelement(XHTML('a')) li.append(a) href = container.name_to_href(entry['dest'], tocname) if entry['frag']: href += '#' + entry['frag'] a.set('href', href) return a def set_landmarks(container, root, tocname, landmarks): nav = ensure_single_nav_of_type(root, 'landmarks') nav.set('hidden', '') ol = nav.makeelement(XHTML('ol')) nav.append(ol) for entry in landmarks: if entry['type'] and container.has_name(entry['dest']) and container.mime_map[entry['dest']] in OEB_DOCS: a = create_nav_li(container, ol, entry, tocname) a.set('{%s}type' % EPUB_NS, entry['type']) a.text = entry['title'] or None pretty_xml_tree(nav) collapse_li(nav)

def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None): tocname, root = ensure_container_has_nav(container, lang=lang, previous_nav=previous_nav) nav = ensure_single_nav_of_type(root, 'toc') if toc.toc_title: nav.append(nav.makeelement(XHTML('h1'))) nav[-1].text = toc.toc_title rnode = nav.makeelement(XHTML('ol')) nav.append(rnode) to_href = partial(container.name_to_href, base=tocname) spat = re.compile(r'\s+')

def process_node(xml_parent, toc_parent): for child in toc_parent: li = xml_parent.makeelement(XHTML('li')) xml_parent.append(li) title = child.title or '' title = spat.sub(' ', title).strip() a = li.makeelement(XHTML('a' if child.dest else 'span')) a.text = title li.append(a) if child.dest: href = to_href(child.dest) if child.frag: href += '#'+child.frag a.set('href', href) if len(child): ol = li.makeelement(XHTML('ol')) li.append(ol) process_node(ol, child) process_node(rnode, toc) pretty_xml_tree(nav)

collapse_li(nav) nav.tail = '\n'

if toc.page_list: nav = ensure_single_nav_of_type(root, 'page-list') nav.set('hidden', '') ol = nav.makeelement(XHTML('ol')) nav.append(ol) for entry in toc.page_list: if container.has_name(entry['dest']) and container.mime_map[entry['dest']] in OEB_DOCS: a = create_nav_li(container, ol, entry, tocname) a.text = str(entry['pagenum']) pretty_xml_tree(nav) collapse_li(nav) container.replace(tocname, root)

def commit_toc(container, toc, lang=None, uid=None): commit_ncx_toc(container, toc, lang=lang, uid=uid) if container.opf_version_parsed.major > 2: commit_nav_toc(container, toc, lang=lang)

def remove_names_from_toc(container, names): changed = [] names = frozenset(names) for find_toc, parse_toc, commit_toc in ( (find_existing_ncx_toc, parse_ncx, commit_ncx_toc), (find_existing_nav_toc, parse_nav, commit_nav_toc), ): toc = get_x_toc(container, find_toc, parse_toc, verify_destinations=False) if len(toc) > 0: remove = [] for node in toc.iterdescendants(): if node.dest in names: remove.append(node) if remove: for node in reversed(remove): node.remove_from_parent() commit_toc(container, toc) changed.append(find_toc(container)) return changed def find_inline_toc(container): for name, linear in container.spine_names: if container.parsed(name).xpath('//*[local-name()="body" and @id="calibre_generated_inline_toc"]'): return name def toc_to_html(toc, container, toc_name, title, lang=None):

def process_node(html_parent, toc, level=1, indent=' ', style_level=2): li = html_parent.makeelement(XHTML('li')) li.tail = '\n'+ (indent*level) html_parent.append(li) name, frag = toc.dest, toc.frag href = '#' if name: href = container.name_to_href(name, toc_name) if frag: href += '#' + frag a = li.makeelement(XHTML('a'), href=href) a.text = toc.title li.append(a) if len(toc) > 0: parent = li.makeelement(XHTML('ul')) parent.set('class', 'level%d' % (style_level)) li.append(parent) a.tail = '\n\n' + (indent*(level+2)) parent.text = '\n'+(indent*(level+3)) parent.tail = '\n\n' + (indent*(level+1)) for child in toc: process_node(parent, child, level+3, style_level=style_level + 1) parent[-1].tail = '\n' + (indent*(level+2))

E = ElementMaker(namespace=XHTML_NS, nsmap={None:XHTML_NS}) html = E.html( E.head( E.title(title), E.style(P('templates/inline_toc_styles.css', data=True).decode('utf-8'), type='text/css'), ), E.body( E.h2(title), E.ul(), id="calibre_generated_inline_toc", ) )

ul = html[1][1] ul.set('class', 'level1') for child in toc: process_node(ul, child) if lang: html.set('lang', lang) pretty_html_tree(container, html) return html

[belgeler] def create_inline_toc(container, title=None): ''' Create an inline (HTML) Table of Contents from an existing NCX Table of Contents.

:param title: The title for this table of contents. ''' lang = get_book_language(container) default_title = 'Table of Contents' if lang: lang = lang_as_iso639_1(lang) or lang default_title = translate(lang, default_title) title = title or default_title toc = get_toc(container) if len(toc) == 0: return None toc_name = find_inline_toc(container)

name = toc_name html = toc_to_html(toc, container, name, title, lang) raw = serialize(html, 'text/html') if name is None: name, c = 'toc.xhtml', 0 while container.has_name(name): c += 1 name = 'toc%d.xhtml' % c container.add_file(name, raw, spine_index=0) else: with container.open(name, 'wb') as f: f.write(raw) set_guide_item(container, 'toc', title, name, frag='calibre_generated_inline_toc') return name

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.gui2.actions

calibre.gui2.actions öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en'

from functools import partial from zipfile import ZipFile from qt.core import QAction, QIcon, QKeySequence, QMenu, QObject, QPoint, QTimer, QToolButton from calibre import prints from calibre.constants import ismacos from calibre.gui2 import Dispatcher from calibre.gui2.keyboard import NameConflict from polyglot.builtins import string_or_bytes def toolbar_widgets_for_action(gui, action): # Search the toolbars for the widget associated with an action, passing # them to the caller for further processing for x in gui.bars_manager.bars: try: w = x.widgetForAction(action) # It seems that multiple copies of the action can exist, such as # when the device-connected menu is changed while the device is # connected. Use the one that has an actual position. if w is None or w.pos().x() == 0: continue # The button might be hidden if not w.isVisible(): continue yield(w) except Exception: continue def show_menu_under_widget(gui, menu, action, name): # First try the tool bar for w in toolbar_widgets_for_action(gui, action): try: # The w.height() assures that the menu opens below the button. menu.exec(w.mapToGlobal(QPoint(0, w.height()))) return except Exception: continue # Now try the menu bar for x in gui.bars_manager.menu_bar.added_actions: # This depends on no two menus with the same name. # I don't know if this works on a Mac if x.text() == name: try: # The menu item might be hidden if not x.isVisible(): continue # We can't use x.trigger() because it doesn't put the menu # in the right place. Instead get the position of the menu # widget on the menu bar p = x.parent().menu_bar r = p.actionGeometry(x) # Make sure that the menu item is actually displayed in the menu # and not the overflow if p.geometry().width() < (r.x() + r.width()): continue # Show the menu under the name in the menu bar menu.exec(p.mapToGlobal(QPoint(r.x()+2, r.height()-2))) return except Exception: continue # No visible button found. Fall back to displaying in upper left corner # of the library view. menu.exec(gui.library_view.mapToGlobal(QPoint(10, 10)))

def menu_action_unique_name(plugin, unique_name): return '%s : menu action : %s'%(plugin.unique_name, unique_name)

[belgeler] class InterfaceAction(QObject):

''' A plugin representing an "action" that can be taken in the graphical user interface. All the items in the toolbar and context menus are implemented by these plugins.

Note that this class is the base class for these plugins, however, to integrate the plugin with calibre's plugin system, you have to make a wrapper class that references the actual plugin. See the :mod:`calibre.customize.builtins` module for examples.

If two :class:`InterfaceAction` objects have the same name, the one with higher priority takes precedence.

Sub-classes should implement the :meth:`genesis`, :meth:`library_changed`, :meth:`location_selected`, :meth:`shutting_down`, :meth:`initialization_complete` and :meth:`tag_browser_context_action` methods.

Once initialized, this plugin has access to the main calibre GUI via the :attr:`gui` member. You can access other plugins by name, for example::

self.gui.iactions['Save To Disk']

To access the actual plugin, use the :attr:`interface_action_base_plugin` attribute, this attribute only becomes available after the plugin has been initialized. Useful if you want to use methods from the plugin class like do_user_config().

The QAction specified by :attr:`action_spec` is automatically create and made available as ``self.qaction``.

'''

#: The plugin name. If two plugins with the same name are present, the one #: with higher priority takes precedence. name = 'Implement me'

#: The plugin priority. If two plugins with the same name are present, the one #: with higher priority takes precedence. priority = 1

#: The menu popup type for when this plugin is added to a toolbar popup_type = QToolButton.ToolButtonPopupMode.MenuButtonPopup

#: Whether this action should be auto repeated when its shortcut #: key is held down. auto_repeat = False

#: Of the form: (text, icon_path, tooltip, keyboard shortcut). #: icon, tooltip and keyboard shortcut can be None. #: keyboard shortcut must be either a string, None or tuple of shortcuts. #: If None, a keyboard shortcut corresponding to the action is not #: registered. If you pass an empty tuple, then the shortcut is registered #: with no default key binding. action_spec = ('text', 'icon', None, None)

#: If not None, used for the name displayed to the user when customizing #: the keyboard shortcuts for the above action spec instead of action_spec[0] action_shortcut_name = None

#: If True, a menu is automatically created and added to self.qaction action_add_menu = False

#: If True, a clone of self.qaction is added to the menu of self.qaction #: If you want the text of this action to be different from that of #: self.qaction, set this variable to the new text action_menu_clone_qaction = False

#: Set of locations to which this action must not be added. #: See :attr:`all_locations` for a list of possible locations dont_add_to = frozenset()

#: Set of locations from which this action must not be removed. #: See :attr:`all_locations` for a list of possible locations dont_remove_from = frozenset()

all_locations = frozenset(['toolbar', 'toolbar-device', 'context-menu', 'context-menu-device', 'toolbar-child', 'menubar', 'menubar-device', 'context-menu-cover-browser', 'context-menu-split', 'searchbar'])

#: Type of action #: 'current' means acts on the current view #: 'global' means an action that does not act on the current view, but rather #: on calibre as a whole action_type = 'global'

#: If True, then this InterfaceAction will have the opportunity to interact #: with drag and drop events. See the methods, :meth:`accept_enter_event`, #: :meth`:accept_drag_move_event`, :meth:`drop_event` for details. accepts_drops = False def __init__(self, parent, site_customization): QObject.__init__(self, parent) self.setObjectName(self.name) self.gui = parent self.site_customization = site_customization self.interface_action_base_plugin = None

[belgeler] def accept_enter_event(self, event, mime_data): ''' This method should return True iff this interface action is capable of handling the drag event. Do not call accept/ignore on the event, that will be taken care of by the calibre UI.''' return False

[belgeler] def accept_drag_move_event(self, event, mime_data): ''' This method should return True iff this interface action is capable of handling the drag event. Do not call accept/ignore on the event, that will be taken care of by the calibre UI.''' return False

[belgeler] def drop_event(self, event, mime_data): ''' This method should perform some useful action and return True iff this interface action is capable of handling the drop event. Do not call accept/ignore on the event, that will be taken care of by the calibre UI. You should not perform blocking/long operations in this function. Instead emit a signal or use QTimer.singleShot and return quickly. See the builtin actions for examples.''' return False
def do_genesis(self): self.Dispatcher = partial(Dispatcher, parent=self) self.create_action() self.gui.addAction(self.qaction) self.gui.addAction(self.menuless_qaction) self.genesis() self.location_selected('library')

@property def unique_name(self): bn = self.__class__.__name__ if getattr(self.interface_action_base_plugin, 'name'): bn = self.interface_action_base_plugin.name return 'Interface Action: %s (%s)'%(bn, self.name)

def create_action(self, spec=None, attr='qaction', shortcut_name=None, persist_shortcut=False): if spec is None: spec = self.action_spec text, icon, tooltip, shortcut = spec if icon is not None: action = QAction(QIcon.ic(icon), text, self.gui) else: action = QAction(text, self.gui) if attr == 'qaction': if hasattr(self.action_menu_clone_qaction, 'rstrip'): mt = str(self.action_menu_clone_qaction) else: mt = action.text() self.menuless_qaction = ma = QAction(action.icon(), mt, self.gui) ma.triggered.connect(action.trigger) for a in ((action, ma) if attr == 'qaction' else (action,)): a.setAutoRepeat(self.auto_repeat) text = tooltip if tooltip else text a.setToolTip(text) a.setStatusTip(text) a.setWhatsThis(text) shortcut_action = action desc = tooltip if tooltip else None if attr == 'qaction': shortcut_action = ma if shortcut is not None: keys = ((shortcut,) if isinstance(shortcut, string_or_bytes) else tuple(shortcut)) if shortcut_name is None: if self.action_shortcut_name is not None: shortcut_name = self.action_shortcut_name elif spec[0]: shortcut_name = str(spec[0]) if shortcut_name and self.action_spec[0] and not ( attr == 'qaction' and self.popup_type == QToolButton.ToolButtonPopupMode.InstantPopup): try: self.gui.keyboard.register_shortcut(self.unique_name + ' - ' + attr, shortcut_name, default_keys=keys, action=shortcut_action, description=desc, group=self.action_spec[0], persist_shortcut=persist_shortcut) except NameConflict as e: try: prints(str(e)) except: pass shortcut_action.setShortcuts([QKeySequence(key, QKeySequence.SequenceFormat.PortableText) for key in keys]) else: self.shortcut_action_for_context_menu = shortcut_action if ismacos: # In Qt 5 keyboard shortcuts dont work unless the # action is explicitly added to the main window self.gui.addAction(shortcut_action)

if attr is not None: setattr(self, attr, action) if attr == 'qaction' and self.action_add_menu: menu = QMenu() action.setMenu(menu) if self.action_menu_clone_qaction: menu.addAction(self.menuless_qaction) return action

[belgeler] def create_menu_action(self, menu, unique_name, text, icon=None, shortcut=None, description=None, triggered=None, shortcut_name=None, persist_shortcut=False): ''' Convenience method to easily add actions to a QMenu. Returns the created QAction. This action has one extra attribute calibre_shortcut_unique_name which if not None refers to the unique name under which this action is registered with the keyboard manager.

:param menu: The QMenu the newly created action will be added to :param unique_name: A unique name for this action, this must be globally unique, so make it as descriptive as possible. If in doubt, add an UUID to it. :param text: The text of the action. :param icon: Either a QIcon or a file name. The file name is passed to the QIcon.ic() builtin, so you do not need to pass the full path to the images folder. :param shortcut: A string, a list of strings, None or False. If False, no keyboard shortcut is registered for this action. If None, a keyboard shortcut with no default keybinding is registered. String and list of strings register a shortcut with default keybinding as specified. :param description: A description for this action. Used to set tooltips. :param triggered: A callable which is connected to the triggered signal of the created action. :param shortcut_name: The text displayed to the user when customizing the keyboard shortcuts for this action. By default it is set to the value of ``text``. :param persist_shortcut: Shortcuts for actions that don't always appear, or are library dependent, may disappear when other keyboard shortcuts are edited unless ```persist_shortcut``` is set True.

''' if shortcut_name is None: shortcut_name = str(text) ac = menu.addAction(text) if icon is not None: if not isinstance(icon, QIcon): icon = QIcon.ic(icon) ac.setIcon(icon) keys = () if shortcut is not None and shortcut is not False: keys = ((shortcut,) if isinstance(shortcut, string_or_bytes) else tuple(shortcut)) unique_name = menu_action_unique_name(self, unique_name) if description is not None: ac.setToolTip(description) ac.setStatusTip(description) ac.setWhatsThis(description)

ac.calibre_shortcut_unique_name = unique_name if shortcut is not False: self.gui.keyboard.register_shortcut(unique_name, shortcut_name, default_keys=keys, action=ac, description=description, group=self.action_spec[0], persist_shortcut=persist_shortcut) # In Qt 5 keyboard shortcuts dont work unless the # action is explicitly added to the main window and on OSX and # Unity since the menu might be exported, the shortcuts won't work self.gui.addAction(ac) if triggered is not None: ac.triggered.connect(triggered) return ac

[belgeler] def load_resources(self, names): ''' If this plugin comes in a ZIP file (user added plugin), this method will allow you to load resources from the ZIP file.

For example to load an image::

pixmap = QPixmap() pixmap.loadFromData(tuple(self.load_resources(['images/icon.png']).values())[0]) icon = QIcon(pixmap)

:param names: List of paths to resources in the ZIP file using / as separator

:return: A dictionary of the form ``{name : file_contents}``. Any names that were not found in the ZIP file will not be present in the dictionary.

''' if self.plugin_path is None: raise ValueError('This plugin was not loaded from a ZIP file') ans = {} with ZipFile(self.plugin_path, 'r') as zf: for candidate in zf.namelist(): if candidate in names: ans[candidate] = zf.read(candidate) return ans

[belgeler] def genesis(self): ''' Setup this plugin. Only called once during initialization. self.gui is available. The action specified by :attr:`action_spec` is available as ``self.qaction``. ''' pass

[belgeler] def location_selected(self, loc): ''' Called whenever the book list being displayed in calibre changes. Currently values for loc are: ``library, main, card and cardb``.

This method should enable/disable this action and its sub actions as appropriate for the location. ''' pass

[belgeler] def library_about_to_change(self, olddb, db): ''' Called whenever the current library is changed.

:param olddb: The LibraryDatabase corresponding to the previous library. :param db: The LibraryDatabase corresponding to the new library.

''' pass

[belgeler] def library_changed(self, db): ''' Called whenever the current library is changed.

:param db: The LibraryDatabase corresponding to the current library.

''' pass

[belgeler] def gui_layout_complete(self): ''' Called once per action when the layout of the main GUI is completed. If your action needs to make changes to the layout, they should be done here, rather than in :meth:`initialization_complete`. ''' pass

[belgeler] def initialization_complete(self): ''' Called once per action when the initialization of the main GUI is completed. ''' pass

[belgeler] def tag_browser_context_action(self, index): ''' Called when displaying the context menu in the Tag browser. ``index`` is the QModelIndex that points to the Tag browser item that was right clicked. Test it for validity with index.valid() and get the underlying TagTreeItem object with index.data(Qt.ItemDataRole.UserRole). Any action objects yielded by this method will be added to the context menu. ''' if False: yield QAction()

[belgeler] def shutting_down(self): ''' Called once per plugin when the main GUI is in the process of shutting down. Release any used resources, but try not to block the shutdown for long periods of time. ''' pass
class InterfaceActionWithLibraryDrop(InterfaceAction): ''' Subclass of InterfaceAction that implemente methods to execute the default action by drop some books from the library.

Inside the do_drop() method, the ids of the droped books are provided by the attribute self.dropped_ids '''

accepts_drops = True mimetype_for_drop = 'application/calibre+from_library'

def accept_enter_event(self, event, mime_data): if mime_data.hasFormat(self.mimetype_for_drop): return True return False def accept_drag_move_event(self, event, mime_data): if mime_data.hasFormat(self.mimetype_for_drop): return True return False def drop_event(self, event, mime_data): if mime_data.hasFormat(self.mimetype_for_drop): self.dropped_ids = tuple(map(int, mime_data.data(self.mimetype_for_drop).data().split())) QTimer.singleShot(1, self.do_drop) return True return False def do_drop(self): raise NotImplementedError()

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.gui2.preferences

calibre.gui2.preferences öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import textwrap from qt.core import ( QAbstractSpinBox, QApplication, QCheckBox, QComboBox, QDialog, QDialogButtonBox, QEvent, QIcon, QLineEdit, QListView, QListWidget, Qt, QTableWidget, QVBoxLayout, QWidget, pyqtSignal, )

from calibre.customize.ui import preferences_plugins from calibre.gui2.complete2 import EditWithComplete from calibre.gui2.widgets import HistoryLineEdit from calibre.utils.config import ConfigProxy from polyglot.builtins import string_or_bytes class AbortCommit(Exception): pass class AbortInitialize(Exception): pass

[belgeler] class ConfigWidgetInterface:

''' This class defines the interface that all widgets displayed in the Preferences dialog must implement. See :class:`ConfigWidgetBase` for a base class that implements this interface and defines various convenience methods as well. '''

#: This signal must be emitted whenever the user changes a value in this #: widget changed_signal = None

#: Set to True iff the :meth:`restore_to_defaults` method is implemented. supports_restoring_to_defaults = True

#: The tooltip for the "Restore defaults" button restore_defaults_desc = _('Restore settings to default values. ' 'You have to click Apply to actually save the default settings.')

#: If True the Preferences dialog will not allow the user to set any more #: preferences. Only has effect if :meth:`commit` returns True. restart_critical = False

[belgeler] def genesis(self, gui): ''' Called once before the widget is displayed, should perform any necessary setup.

:param gui: The main calibre graphical user interface ''' raise NotImplementedError()

[belgeler] def initialize(self): ''' Should set all config values to their initial values (the values stored in the config files). A "return" statement is optional. Return False if the dialog is not to be shown. ''' raise NotImplementedError()

[belgeler] def restore_defaults(self): ''' Should set all config values to their defaults. ''' pass

[belgeler] def commit(self): ''' Save any changed settings. Return True if the changes require a restart, False otherwise. Raise an :class:`AbortCommit` exception to indicate that an error occurred. You are responsible for giving the user feedback about what the error is and how to correct it. ''' return False

[belgeler] def refresh_gui(self, gui): ''' Called once after this widget is committed. Responsible for causing the gui to reread any changed settings. Note that by default the GUI re-initializes various elements anyway, so most widgets won't need to use this method. ''' pass

[belgeler] def initial_tab_changed(self): ''' Called if the initially displayed tab is changed before the widget is shown, but after it is initialized. ''' pass

def set_help_tips(gui_obj, tt): if tt: if not str(gui_obj.whatsThis()): gui_obj.setWhatsThis(tt) if not str(gui_obj.statusTip()): gui_obj.setStatusTip(tt) tt = '\n'.join(textwrap.wrap(tt, 70)) gui_obj.setToolTip(tt)

class Setting:

CHOICES_SEARCH_FLAGS = Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive def __init__(self, name, config_obj, widget, gui_name=None, empty_string_is_None=True, choices=None, restart_required=False): self.name, self.gui_name = name, gui_name self.empty_string_is_None = empty_string_is_None self.restart_required = restart_required self.choices = choices if gui_name is None: self.gui_name = 'opt_'+name self.config_obj = config_obj self.gui_obj = getattr(widget, self.gui_name) self.widget = widget if isinstance(self.gui_obj, QCheckBox): self.datatype = 'bool' self.gui_obj.stateChanged.connect(self.changed) elif isinstance(self.gui_obj, QAbstractSpinBox): self.datatype = 'number' self.gui_obj.valueChanged.connect(self.changed) elif isinstance(self.gui_obj, (QLineEdit, HistoryLineEdit)): self.datatype = 'string' self.gui_obj.textChanged.connect(self.changed) if isinstance(self.gui_obj, HistoryLineEdit): self.gui_obj.initialize('preferences_setting_' + self.name) elif isinstance(self.gui_obj, QComboBox): self.datatype = 'choice' self.gui_obj.editTextChanged.connect(self.changed) self.gui_obj.currentIndexChanged.connect(self.changed) else: raise ValueError('Unknown data type %s' % self.gui_obj.__class__)

if isinstance(self.config_obj, ConfigProxy) and \ not str(self.gui_obj.toolTip()): h = self.config_obj.help(self.name) if h: self.gui_obj.setToolTip(h) tt = str(self.gui_obj.toolTip()) set_help_tips(self.gui_obj, tt)

def changed(self, *args): self.widget.changed_signal.emit()

def initialize(self): self.gui_obj.blockSignals(True) if self.datatype == 'choice': choices = self.choices or [] if isinstance(self.gui_obj, EditWithComplete): self.gui_obj.all_items = choices else: self.gui_obj.clear() for x in choices: if isinstance(x, string_or_bytes): x = (x, x) self.gui_obj.addItem(x[0], (x[1])) self.set_gui_val(self.get_config_val(default=False)) self.gui_obj.blockSignals(False) self.initial_value = self.get_gui_val()

def commit(self): val = self.get_gui_val() oldval = self.get_config_val() changed = val != oldval if changed: self.set_config_val(self.get_gui_val()) return changed and self.restart_required def restore_defaults(self): self.set_gui_val(self.get_config_val(default=True))

def get_config_val(self, default=False): if default: val = self.config_obj.defaults[self.name] else: val = self.config_obj[self.name] return val def set_config_val(self, val): self.config_obj[self.name] = val def set_gui_val(self, val): if self.datatype == 'bool': self.gui_obj.setChecked(bool(val)) elif self.datatype == 'number': self.gui_obj.setValue(val) elif self.datatype == 'string': self.gui_obj.setText(val if val else '') elif self.datatype == 'choice': if isinstance(self.gui_obj, EditWithComplete): self.gui_obj.setText(val) else: idx = self.gui_obj.findData((val), role=Qt.ItemDataRole.UserRole, flags=self.CHOICES_SEARCH_FLAGS) if idx == -1: idx = 0 self.gui_obj.setCurrentIndex(idx)

def get_gui_val(self): if self.datatype == 'bool': val = bool(self.gui_obj.isChecked()) elif self.datatype == 'number': val = self.gui_obj.value() elif self.datatype == 'string': val = str(self.gui_obj.text()).strip() if self.empty_string_is_None and not val: val = None elif self.datatype == 'choice': if isinstance(self.gui_obj, EditWithComplete): val = str(self.gui_obj.text()) else: idx = self.gui_obj.currentIndex() if idx < 0: idx = 0 val = str(self.gui_obj.itemData(idx) or '') return val class CommaSeparatedList(Setting):

def set_gui_val(self, val): x = '' if val: x = ', '.join(val) self.gui_obj.setText(x)

def get_gui_val(self): val = str(self.gui_obj.text()).strip() ans = [] if val: ans = [x.strip() for x in val.split(',')] ans = [x for x in ans if x] return ans

[belgeler] class ConfigWidgetBase(QWidget, ConfigWidgetInterface):

''' Base class that contains code to easily add standard config widgets like checkboxes, combo boxes, text fields and so on. See the :meth:`register` method.

This class automatically handles change notification, resetting to default, translation between gui objects and config objects, etc. for registered settings.

If your config widget inherits from this class but includes setting that are not registered, you should override the :class:`ConfigWidgetInterface` methods and call the base class methods inside the overrides. '''

changed_signal = pyqtSignal() restart_now = pyqtSignal() supports_restoring_to_defaults = True restart_critical = False def __init__(self, parent=None): QWidget.__init__(self, parent) if hasattr(self, 'setupUi'): self.setupUi(self) self.settings = {}

[belgeler] def register(self, name, config_obj, gui_name=None, choices=None, restart_required=False, empty_string_is_None=True, setting=Setting): ''' Register a setting.

:param name: The setting name :param config_obj: The config object that reads/writes the setting :param gui_name: The name of the GUI object that presents an interface to change the setting. By default it is assumed to be ``'opt_' + name``. :param choices: If this setting is a multiple choice (combobox) based setting, the list of choices. The list is a list of two element tuples of the form: ``[(gui name, value), ...]`` :param setting: The class responsible for managing this setting. The default class handles almost all cases, so this param is rarely used. ''' setting = setting(name, config_obj, self, gui_name=gui_name, choices=choices, restart_required=restart_required, empty_string_is_None=empty_string_is_None) return self.register_setting(setting)

def register_setting(self, setting): self.settings[setting.name] = setting return setting

[belgeler] def initialize(self): for setting in self.settings.values(): setting.initialize()

[belgeler] def commit(self, *args): restart_required = False for setting in self.settings.values(): rr = setting.commit() if rr: restart_required = True return restart_required

[belgeler] def restore_defaults(self, *args): for setting in self.settings.values(): setting.restore_defaults()

def get_plugin(category, name): for plugin in preferences_plugins(): if plugin.category == category and plugin.name == name: return plugin raise ValueError( 'No Preferences Plugin with category: %s and name: %s found' % (category, name))

class ConfigDialog(QDialog):

def set_widget(self, w): self.w = w def accept(self): try: self.restart_required = self.w.commit() except AbortCommit: return QDialog.accept(self)

def init_gui(): from calibre.gui2.main import option_parser from calibre.gui2.ui import Main from calibre.library import db parser = option_parser() opts, args = parser.parse_args([]) actions = tuple(Main.create_application_menubar()) db = db() gui = Main(opts) gui.initialize(db.library_path, db, actions, show_gui=False) return gui def show_config_widget(category, name, gui=None, show_restart_msg=False, parent=None, never_shutdown=False): ''' Show the preferences plugin identified by category and name

:param gui: gui instance, if None a hidden gui is created :param show_restart_msg: If True and the preferences plugin indicates a restart is required, show a message box telling the user to restart :param parent: The parent of the displayed dialog

:return: True iff a restart is required for the changes made by the user to take effect ''' from calibre.gui2 import gprefs pl = get_plugin(category, name) d = ConfigDialog(parent) d.resize(750, 550) conf_name = 'config_widget_dialog_geometry_%s_%s'%(category, name) d.setWindowTitle(_('Configure ') + pl.gui_name) d.setWindowIcon(QIcon.ic('config.png')) bb = QDialogButtonBox(d) bb.setStandardButtons(QDialogButtonBox.StandardButton.Apply|QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.RestoreDefaults) bb.accepted.connect(d.accept) bb.rejected.connect(d.reject) w = pl.create_widget(d) d.set_widget(w) bb.button(QDialogButtonBox.StandardButton.RestoreDefaults).clicked.connect(w.restore_defaults) bb.button(QDialogButtonBox.StandardButton.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults) bb.button(QDialogButtonBox.StandardButton.Apply).setEnabled(False) bb.button(QDialogButtonBox.StandardButton.Apply).clicked.connect(d.accept)

def onchange(): b = bb.button(QDialogButtonBox.StandardButton.Apply) b.setEnabled(True) b.setDefault(True) b.setAutoDefault(True) w.changed_signal.connect(onchange) bb.button(QDialogButtonBox.StandardButton.Cancel).setFocus(Qt.FocusReason.OtherFocusReason) l = QVBoxLayout() d.setLayout(l) l.addWidget(w) l.addWidget(bb) mygui = gui is None if gui is None: gui = init_gui() mygui = True w.genesis(gui) w.initialize() d.restore_geometry(gprefs, conf_name) d.exec() d.save_geometry(gprefs, conf_name) rr = getattr(d, 'restart_required', False) if show_restart_msg and rr: from calibre.gui2 import warning_dialog warning_dialog(gui, 'Restart required', 'Restart required', show=True) if mygui and not never_shutdown: gui.shutdown() return rr class ListViewWithMoveByKeyPress(QListView):

def set_movement_functions(self, up_function, down_function): self.up_function = up_function self.down_function = down_function def event(self, event): if (event.type() == QEvent.KeyPress and QApplication.keyboardModifiers() == Qt.KeyboardModifier.ControlModifier): if event.key() == Qt.Key.Key_Up: self.up_function() elif event.key() == Qt.Key.Key_Down: self.down_function() return True return QListView.event(self, event)

class ListWidgetWithMoveByKeyPress(QListWidget):

def set_movement_functions(self, up_function, down_function): self.up_function = up_function self.down_function = down_function def event(self, event): if (event.type() == QEvent.KeyPress and QApplication.keyboardModifiers() == Qt.KeyboardModifier.ControlModifier): if event.key() == Qt.Key.Key_Up: self.up_function() elif event.key() == Qt.Key.Key_Down: self.down_function() return True return QListWidget.event(self, event)

class TableWidgetWithMoveByKeyPress(QTableWidget):

def set_movement_functions(self, up_function, down_function): self.up_function = up_function self.down_function = down_function def event(self, event): if (event.type() == QEvent.KeyPress and QApplication.keyboardModifiers() == Qt.KeyboardModifier.ControlModifier): if event.key() == Qt.Key.Key_Up: self.up_function() elif event.key() == Qt.Key.Key_Down: self.down_function() return True return QTableWidget.event(self, event)

# Testing {{{ def test_widget(category, name, gui=None): show_config_widget(category, name, gui=gui, show_restart_msg=True)

def test_all(): from qt.core import QApplication app = QApplication([]) app gui = init_gui() for plugin in preferences_plugins(): test_widget(plugin.category, plugin.name, gui=gui) gui.shutdown()

if __name__ == '__main__': test_all() # }}}

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.gui2.tweak_book.boss

calibre.gui2.tweak_book.boss öğesinin kaynak kodu

#!/usr/bin/env python # License: GPLv3 Copyright: 2013, Kovid Goyal <kovid at kovidgoyal.net> import errno import os import shutil import subprocess import sys import tempfile from functools import partial, wraps from qt.core import ( QApplication, QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QIcon, QInputDialog, QLabel, QMimeData, QObject, QSize, Qt, QTimer, QUrl, QVBoxLayout, pyqtSignal, )

from calibre import isbytestring, prints from calibre.constants import cache_dir, islinux, ismacos, iswindows from calibre.ebooks.oeb.base import urlnormalize from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES, clone_container, guess_type from calibre.ebooks.oeb.polish.container import get_container as _gc from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage, set_cover from calibre.ebooks.oeb.polish.css import filter_css, rename_class from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all from calibre.ebooks.oeb.polish.replace import get_recommended_folders, rationalize_folders, rename_files, replace_file from calibre.ebooks.oeb.polish.split import AbortError, merge, multisplit, split from calibre.ebooks.oeb.polish.toc import create_inline_toc, mark_as_nav, remove_names_from_toc from calibre.ebooks.oeb.polish.utils import link_stylesheets from calibre.ebooks.oeb.polish.utils import setup_css_parser_serialization as scs from calibre.gui2 import ( add_to_recent_docs, choose_dir, choose_files, choose_save_file, error_dialog, info_dialog, open_url, question_dialog, sanitize_env_vars, warning_dialog, ) from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.tweak_book import actions, current_container, dictionaries, editor_name, editors, set_book_locale, set_current_container, tprefs from calibre.gui2.tweak_book.completion.worker import completion_worker from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime from calibre.gui2.tweak_book.editor.insert_resource import NewBook, get_resource_data from calibre.gui2.tweak_book.file_list import FILE_COPY_MIME, NewFileDialog from calibre.gui2.tweak_book.preferences import Preferences from calibre.gui2.tweak_book.preview import parse_worker from calibre.gui2.tweak_book.save import SaveManager, find_first_existing_ancestor, save_container from calibre.gui2.tweak_book.search import run_search, validate_search_request from calibre.gui2.tweak_book.spell import find_next as find_next_word from calibre.gui2.tweak_book.spell import find_next_error from calibre.gui2.tweak_book.toc import TOCEditor from calibre.gui2.tweak_book.undo import GlobalUndoHistory from calibre.gui2.tweak_book.widgets import ( AddCover, FilterCSS, ImportForeign, InsertLink, InsertSemantics, InsertTag, MultiSplit, QuickOpen, RationalizeFolders, ) from calibre.gui2.widgets import BusyCursor from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory from calibre.startup import connect_lambda from calibre.utils.config import JSONConfig from calibre.utils.icu import numeric_sort_key from calibre.utils.imghdr import identify from calibre.utils.ipc.launch import exe_path, macos_edit_book_bundle_path from calibre.utils.localization import ngettext from calibre.utils.tdir_in_cache import tdir_in_cache from polyglot.builtins import as_bytes, iteritems, itervalues, string_or_bytes from polyglot.urllib import urlparse

_diff_dialogs = [] last_used_transform_rules = [] last_used_html_transform_rules = []

def get_container(*args, **kwargs): kwargs['tweak_mode'] = True container = _gc(*args, **kwargs) return container def setup_css_parser_serialization(): scs(tprefs['editor_tab_stop_width'])

def in_thread_job(func): @wraps(func) def ans(*args, **kwargs): with BusyCursor(): return func(*args, **kwargs) return ans def get_boss(): return get_boss.boss

[belgeler] class Boss(QObject):

handle_completion_result_signal = pyqtSignal(object)

def __init__(self, parent, notify=None): QObject.__init__(self, parent) self.global_undo = GlobalUndoHistory() self.file_was_readonly = False self.container_count = 0 self.tdir = None self.save_manager = SaveManager(parent, notify) self.save_manager.report_error.connect(self.report_save_error) self.save_manager.check_for_completion.connect(self.check_terminal_save) self.doing_terminal_save = False self.ignore_preview_to_editor_sync = False setup_css_parser_serialization() get_boss.boss = self self.gui = parent completion_worker().result_callback = self.handle_completion_result_signal.emit self.handle_completion_result_signal.connect(self.handle_completion_result, Qt.ConnectionType.QueuedConnection) self.completion_request_count = 0 self.editor_cache = JSONConfig('editor-cache', base_path=cache_dir()) d = self.editor_cache.defaults d['edit_book_state'] = {} d['edit_book_state_order'] = []

def __call__(self, gui): self.gui = gui gui.message_popup.undo_requested.connect(self.do_global_undo) fl = gui.file_list fl.delete_requested.connect(self.delete_requested) fl.reorder_spine.connect(self.reorder_spine) fl.rename_requested.connect(self.rename_requested) fl.bulk_rename_requested.connect(self.bulk_rename_requested) fl.edit_file.connect(self.edit_file_requested) fl.merge_requested.connect(self.merge_requested) fl.mark_requested.connect(self.mark_requested) fl.export_requested.connect(self.export_requested) fl.replace_requested.connect(self.replace_requested) fl.link_stylesheets_requested.connect(self.link_stylesheets_requested) fl.initiate_file_copy.connect(self.copy_files_to_clipboard) fl.initiate_file_paste.connect(self.paste_files_from_clipboard) fl.open_file_with.connect(self.open_file_with) self.gui.central.current_editor_changed.connect(self.apply_current_editor_state) self.gui.central.close_requested.connect(self.editor_close_requested) self.gui.central.search_panel.search_triggered.connect(self.search) self.gui.text_search.find_text.connect(self.find_text) self.gui.preview.sync_requested.connect(self.sync_editor_to_preview) self.gui.preview.split_start_requested.connect(self.split_start_requested) self.gui.preview.split_requested.connect(self.split_requested) self.gui.preview.link_clicked.connect(self.link_clicked) self.gui.preview.render_process_restarted.connect(self.report_render_process_restart) self.gui.preview.open_file_with.connect(self.open_file_with) self.gui.preview.edit_file.connect(self.edit_file_requested) self.gui.check_book.item_activated.connect(self.check_item_activated) self.gui.check_book.check_requested.connect(self.check_requested) self.gui.check_book.fix_requested.connect(self.fix_requested) self.gui.toc_view.navigate_requested.connect(self.link_clicked) self.gui.toc_view.refresh_requested.connect(self.commit_all_editors_to_container) self.gui.image_browser.image_activated.connect(self.image_activated) self.gui.checkpoints.revert_requested.connect(self.revert_requested) self.gui.checkpoints.compare_requested.connect(self.compare_requested) self.gui.saved_searches.run_saved_searches.connect(self.run_saved_searches) self.gui.saved_searches.copy_search_to_search_panel.connect(self.gui.central.search_panel.paste_saved_search) self.gui.central.search_panel.save_search.connect(self.save_search) self.gui.central.search_panel.show_saved_searches.connect(self.show_saved_searches) self.gui.spell_check.find_word.connect(self.find_word) self.gui.spell_check.refresh_requested.connect(self.commit_all_editors_to_container) self.gui.spell_check.word_replaced.connect(self.word_replaced) self.gui.spell_check.word_ignored.connect(self.word_ignored) self.gui.spell_check.change_requested.connect(self.word_change_requested) self.gui.live_css.goto_declaration.connect(self.goto_style_declaration) self.gui.manage_fonts.container_changed.connect(self.apply_container_update_to_gui) self.gui.manage_fonts.embed_all_fonts.connect(self.manage_fonts_embed) self.gui.manage_fonts.subset_all_fonts.connect(self.manage_fonts_subset) self.gui.reports.edit_requested.connect(self.reports_edit_requested) self.gui.reports.refresh_starting.connect(self.commit_all_editors_to_container) self.gui.reports.delete_requested.connect(self.delete_requested)

def report_render_process_restart(self): self.gui.show_status_message(_('The Qt WebEngine Render process crashed and has been restarted'))

@property def currently_editing(self): ' Return the name of the file being edited currently or None if no file is being edited ' return editor_name(self.gui.central.current_editor)

def preferences(self): orig_spell = tprefs['inline_spell_check'] orig_size = tprefs['toolbar_icon_size'] p = Preferences(self.gui) ret = p.exec() if p.dictionaries_changed: dictionaries.clear_caches() dictionaries.initialize(force=True) # Reread user dictionaries if p.toolbars_changed: self.gui.populate_toolbars() for ed in itervalues(editors): if hasattr(ed, 'populate_toolbars'): ed.populate_toolbars() if orig_size != tprefs['toolbar_icon_size']: for ed in itervalues(editors): if hasattr(ed, 'bars'): for bar in ed.bars: bar.setIconSize(QSize(tprefs['toolbar_icon_size'], tprefs['toolbar_icon_size']))

if ret == QDialog.DialogCode.Accepted: setup_css_parser_serialization() self.gui.apply_settings() self.refresh_file_list() self.gui.preview.start_refresh_timer() if ret == QDialog.DialogCode.Accepted or p.dictionaries_changed: for ed in itervalues(editors): ed.apply_settings(dictionaries_changed=p.dictionaries_changed) if orig_spell != tprefs['inline_spell_check']: from calibre.gui2.tweak_book.editor.syntax.html import refresh_spell_check_status refresh_spell_check_status() for ed in itervalues(editors): try: ed.editor.highlighter.rehighlight() except AttributeError: pass def mark_requested(self, name, action): self.commit_dirty_opf() c = current_container() if action == 'cover': mark_as_cover(current_container(), name) elif action.startswith('titlepage:'): action, move_to_start = action.partition(':')[0::2] move_to_start = move_to_start == 'True' mark_as_titlepage(current_container(), name, move_to_start=move_to_start) elif action == 'nav': mark_as_nav(current_container(), name)

if c.opf_name in editors: editors[c.opf_name].replace_data(c.raw_data(c.opf_name)) self.gui.file_list.build(c) self.set_modified()

def mkdtemp(self, prefix=''): self.container_count += 1 return tempfile.mkdtemp(prefix='%s%05d-' % (prefix, self.container_count), dir=self.tdir)

def _check_before_open(self): if self.gui.action_save.isEnabled(): if not question_dialog(self.gui, _('Unsaved changes'), _( 'The current book has unsaved changes. If you open a new book, they will be lost.' ' Are you sure you want to proceed?')): return if self.save_manager.has_tasks: return info_dialog(self.gui, _('Cannot open'), _('The current book is being saved, you cannot open a new book until' ' the saving is completed'), show=True) return True def new_book(self): if not self._check_before_open(): return d = NewBook(self.gui) if d.exec() == QDialog.DialogCode.Accepted: fmt = d.fmt.lower() path = choose_save_file(self.gui, 'edit-book-new-book', _('Choose file location'), filters=[(fmt.upper(), (fmt,))], all_files=False) if path is not None: if not path.lower().endswith('.' + fmt): path = path + '.' + fmt from calibre.ebooks.oeb.polish.create import create_book create_book(d.mi, path, fmt=fmt) self.open_book(path=path)

def import_book(self, path=None): if not self._check_before_open(): return d = ImportForeign(self.gui) if hasattr(path, 'rstrip'): d.set_src(os.path.abspath(path)) if d.exec() == QDialog.DialogCode.Accepted: for name in tuple(editors): self.close_editor(name) from calibre.ebooks.oeb.polish.import_book import import_book_as_epub src, dest = d.data self._clear_notify_data = True def func(src, dest, tdir): import_book_as_epub(src, dest) return get_container(dest, tdir=tdir) self.gui.blocking_job('import_book', _('Importing book, please wait...'), self.book_opened, func, src, dest, tdir=self.mkdtemp())

[belgeler] def open_book(self, path=None, edit_file=None, clear_notify_data=True, open_folder=False, search_text=None): ''' Open the e-book at ``path`` for editing. Will show an error if the e-book is not in a supported format or the current book has unsaved changes.

:param edit_file: The name of a file inside the newly opened book to start editing. Can also be a list of names. ''' if isinstance(path, (list, tuple)) and path: # Can happen from an file_event_hook on OS X when drag and dropping # onto the icon in the dock or using open -a path = path[-1] if not self._check_before_open(): return if not hasattr(path, 'rpartition'): if open_folder: path = choose_dir(self.gui, 'open-book-folder-for-tweaking', _('Choose book folder')) if path: path = [path] else: path = choose_files(self.gui, 'open-book-for-tweaking', _('Choose book'), [(_('Books'), [x.lower() for x in SUPPORTED])], all_files=False, select_only_single_file=True)

if not path: return path = path[0]

if not os.path.exists(path): return error_dialog(self.gui, _('File not found'), _( 'The file %s does not exist.') % path, show=True) isdir = os.path.isdir(path) ext = path.rpartition('.')[-1].upper() if ext not in SUPPORTED and not isdir: from calibre.ebooks.oeb.polish.import_book import IMPORTABLE if ext.lower() in IMPORTABLE: return self.import_book(path) return error_dialog(self.gui, _('Unsupported format'), _('Tweaking is only supported for books in the %s formats.' ' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)), show=True)

self.file_was_readonly = not os.access(path, os.W_OK) if self.file_was_readonly: warning_dialog(self.gui, _('Read-only file'), _( 'The file {} is read-only. Saving changes to it will either fail or cause its permissions to be reset.').format(path), show=True)

for name in tuple(editors): self.close_editor(name) self.gui.preview.clear() self.gui.live_css.clear() self.container_count = -1 if self.tdir: shutil.rmtree(self.tdir, ignore_errors=True) # We use the cache dir rather than the temporary dir to try and prevent # temp file cleaners from nuking ebooks. See https://bugs.launchpad.net/bugs/1740460 self.tdir = tdir_in_cache('ee') self._edit_file_on_open = edit_file self._search_text_on_open = search_text self._clear_notify_data = clear_notify_data self.gui.blocking_job('open_book', _('Opening book, please wait...'), self.book_opened, get_container, path, tdir=self.mkdtemp())def book_opened(self, job): ef = getattr(self, '_edit_file_on_open', None) cn = getattr(self, '_clear_notify_data', True) st = getattr(self, '_search_text_on_open', None) self._edit_file_on_open = self._search_text_on_open = None if job.traceback is not None: self.gui.update_status_bar_default_message() if 'DRMError:' in job.traceback: from calibre.gui2.dialogs.drm_error import DRMErrorMessage return DRMErrorMessage(self.gui).exec() if 'ObfuscationKeyMissing:' in job.traceback: return error_dialog(self.gui, _('Failed to open book'), _( 'Failed to open book, it has obfuscated fonts, but the obfuscation key is missing from the OPF.' ' Do an EPUB to EPUB conversion before trying to edit this book.'), show=True)

return error_dialog(self.gui, _('Failed to open book'), _('Failed to open book, click "Show details" for more information.'), det_msg=job.traceback, show=True) if cn: self.save_manager.clear_notify_data() self.gui.check_book.clear_at_startup() self.gui.spell_check.clear_caches() dictionaries.clear_ignored(), dictionaries.clear_caches() parse_worker.clear() container = job.result set_current_container(container) completion_worker().clear_caches() with BusyCursor(): self.current_metadata = self.gui.current_metadata = container.mi lang = container.opf_xpath('//dc:language/text()') or [self.current_metadata.language] set_book_locale(lang[0]) self.global_undo.open_book(container) self.gui.update_window_title() self.gui.file_list.current_edited_name = None self.gui.file_list.build(container, preserve_state=False) self.gui.action_save.setEnabled(False) self.update_global_history_actions() recent_books = list(tprefs.get('recent-books', [])) path = os.path.abspath(container.path_to_ebook) if path in recent_books: recent_books.remove(path) self.gui.update_status_bar_default_message(path) recent_books.insert(0, path) tprefs['recent-books'] = recent_books[:10] self.gui.update_recent_books() if iswindows: try: add_to_recent_docs(path) except Exception: import traceback traceback.print_exc() if ef: if isinstance(ef, str): ef = [ef] for i in ef: self.gui.file_list.request_edit(i) else: if tprefs['restore_book_state']: self.restore_book_edit_state() self.gui.toc_view.update_if_visible() self.add_savepoint(_('Start of editing session')) if st: self.find_initial_text(st)

def update_editors_from_container(self, container=None, names=None): c = container or current_container() for name, ed in tuple(iteritems(editors)): if c.has_name(name): if names is None or name in names: ed.replace_data(c.raw_data(name)) ed.is_synced_to_container = True else: self.close_editor(name)

def refresh_file_list(self): container = current_container() self.gui.file_list.build(container) completion_worker().clear_caches('names')

[belgeler] def apply_container_update_to_gui(self, mark_as_modified=True): ''' Update all the components of the user interface to reflect the latest data in the current book container.

:param mark_as_modified: If True, the book will be marked as modified, so the user will be prompted to save it when quitting. ''' self.refresh_file_list() self.update_global_history_actions() self.update_editors_from_container() if mark_as_modified: self.set_modified() self.gui.toc_view.update_if_visible() completion_worker().clear_caches() self.gui.preview.start_refresh_timer()

@in_thread_job def delete_requested(self, spine_items, other_items): self.add_savepoint(_('Before: Delete files')) self.commit_dirty_opf() c = current_container() c.remove_from_spine(spine_items) for name in other_items: c.remove_item(name) self.set_modified() self.gui.file_list.delete_done(spine_items, other_items) spine_names = [x for x, remove in spine_items if remove] completion_worker().clear_caches('names') items = spine_names + list(other_items) for name in items: if name in editors: self.close_editor(name) if not editors: self.gui.preview.clear() self.gui.live_css.clear() changed = remove_names_from_toc(current_container(), spine_names + list(other_items)) if changed: self.gui.toc_view.update_if_visible() for toc in changed: if toc and toc in editors: editors[toc].replace_data(c.raw_data(toc)) if c.opf_name in editors: editors[c.opf_name].replace_data(c.raw_data(c.opf_name)) self.gui.message_popup(ngettext( 'One file deleted', '{} files deleted', len(items)).format(len(items)))

def commit_dirty_opf(self): c = current_container() if c.opf_name in editors and not editors[c.opf_name].is_synced_to_container: self.commit_editor_to_container(c.opf_name) self.gui.update_window_title()

def reorder_spine(self, items): if not self.ensure_book(): return self.add_savepoint(_('Before: Re-order text')) c = current_container() c.set_spine(items) self.set_modified() self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items if c.opf_name in editors: editors[c.opf_name].replace_data(c.raw_data(c.opf_name)) completion_worker().clear_caches('names')

def add_file(self): if not self.ensure_book(_('You must first open a book to edit, before trying to create new files in it.')): return self.commit_dirty_opf() d = NewFileDialog(self.gui) if d.exec() != QDialog.DialogCode.Accepted: return added_name = self.do_add_file(d.file_name, d.file_data, using_template=d.using_template, edit_file=True) if d.file_name.rpartition('.')[2].lower() in ('ttf', 'otf', 'woff'): from calibre.gui2.tweak_book.manage_fonts import show_font_face_rule_for_font_file show_font_face_rule_for_font_file(d.file_data, added_name, self.gui)

def do_add_file(self, file_name, data, using_template=False, edit_file=False): self.add_savepoint(_('Before: Add file %s') % self.gui.elided_text(file_name)) c = current_container() adata = data.replace(b'%CURSOR%', b'') if using_template else data spine_index = c.index_in_spine(self.currently_editing or '') if spine_index is not None: spine_index += 1 try: added_name = c.add_file(file_name, adata, spine_index=spine_index) except: self.rewind_savepoint() raise self.gui.file_list.build(c) self.gui.file_list.select_name(file_name) if c.opf_name in editors: editors[c.opf_name].replace_data(c.raw_data(c.opf_name)) mt = c.mime_map[file_name] syntax = syntax_from_mime(file_name, mt) if syntax and edit_file: if using_template: self.edit_file(file_name, syntax, use_template=data.decode('utf-8')) else: self.edit_file(file_name, syntax) self.set_modified() completion_worker().clear_caches('names') return added_name def add_files(self): if not self.ensure_book(_('You must first open a book to edit, before trying to create new files in it.')): return files = choose_files(self.gui, 'tweak-book-bulk-import-files', _('Choose files')) if files: folder_map = get_recommended_folders(current_container(), files) files = {x:('/'.join((folder, os.path.basename(x))) if folder else os.path.basename(x)) for x, folder in iteritems(folder_map)} self.add_savepoint(_('Before Add files')) c = current_container() added_fonts = set() for path in sorted(files, key=numeric_sort_key): name = files[path] i = 0 while c.exists(name) or c.manifest_has_name(name) or c.has_name_case_insensitive(name): i += 1 name, ext = name.rpartition('.')[0::2] name = '%s_%d.%s' % (name, i, ext) try: with open(path, 'rb') as f: c.add_file(name, f.read()) except: self.rewind_savepoint() raise if name.rpartition('.')[2].lower() in ('ttf', 'otf', 'woff'): added_fonts.add(name) self.gui.file_list.build(c) if c.opf_name in editors: editors[c.opf_name].replace_data(c.raw_data(c.opf_name)) self.set_modified() completion_worker().clear_caches('names') if added_fonts: from calibre.gui2.tweak_book.manage_fonts import show_font_face_rule_for_font_files show_font_face_rule_for_font_files(c, added_fonts, self.gui)

def add_cover(self): if not self.ensure_book(): return d = AddCover(current_container(), self.gui) d.import_requested.connect(self.do_add_file) try: if d.exec() == QDialog.DialogCode.Accepted and d.file_name is not None: report = [] with BusyCursor(): self.add_savepoint(_('Before: Add cover')) set_cover(current_container(), d.file_name, report.append, options={ 'existing_image':True, 'keep_aspect':tprefs['add_cover_preserve_aspect_ratio']}) self.apply_container_update_to_gui() finally: d.import_requested.disconnect()

def ensure_book(self, msg=None): msg = msg or _('No book is currently open. You must first open a book.') if current_container() is None: error_dialog(self.gui, _('No book open'), msg, show=True) return False return True def edit_toc(self): if not self.ensure_book(_('You must open a book before trying to edit the Table of Contents.')): return self.add_savepoint(_('Before: Edit Table of Contents')) d = TOCEditor(title=self.current_metadata.title, parent=self.gui) if d.exec() != QDialog.DialogCode.Accepted: self.rewind_savepoint() return with BusyCursor(): self.set_modified() self.update_editors_from_container() self.gui.toc_view.update_if_visible() self.gui.file_list.build(current_container())

def insert_inline_toc(self): if not self.ensure_book(): return self.commit_all_editors_to_container() self.add_savepoint(_('Before: Insert inline Table of Contents')) name = create_inline_toc(current_container()) if name is None: self.rewind_savepoint() return error_dialog(self.gui, _('No Table of Contents'), _( 'Cannot create an inline Table of Contents as this book has no existing' ' Table of Contents. You must first create a Table of Contents using the' ' Edit Table of Contents tool.'), show=True) self.apply_container_update_to_gui() self.edit_file(name, 'html')

def polish(self, action, name, parent=None): if not self.ensure_book(): return from calibre.gui2.tweak_book.polish import get_customization, show_report customization = get_customization(action, name, parent or self.gui) if customization is None: return with BusyCursor(): self.add_savepoint(_('Before: %s') % name) try: report, changed = tweak_polish(current_container(), {action:True}, customization=customization) except: self.rewind_savepoint() raise if changed: self.apply_container_update_to_gui() self.gui.update_window_title() if not changed: self.rewind_savepoint() show_report(changed, self.current_metadata.title, report, parent or self.gui, self.show_current_diff)

def transform_html(self): global last_used_html_transform_rules if not self.ensure_book(_('You must first open a book in order to transform styles.')): return from calibre.ebooks.html_transform_rules import transform_container from calibre.gui2.html_transform_rules import RulesDialog d = RulesDialog(self.gui) d.rules = last_used_html_transform_rules d.transform_scope = tprefs['html_transform_scope'] ret = d.exec() last_used_html_transform_rules = d.rules scope = d.transform_scope tprefs.set('html_transform_scope', scope) if ret != QDialog.DialogCode.Accepted: return mime_map = current_container().mime_map names = () if scope == 'current': if not self.currently_editing or mime_map.get(self.currently_editing) not in OEB_DOCS: return error_dialog(self.gui, _('No HTML file'), _('Not currently editing an HTML file'), show=True) names = (self.currently_editing,) elif scope == 'open': names = tuple(name for name in editors if mime_map.get(name) in OEB_DOCS) if not names: return error_dialog(self.gui, _('No HTML files'), _('Not currently editing any HTML files'), show=True) elif scope == 'selected': names = tuple(name for name in self.gui.file_list.file_list.selected_names if mime_map.get(name) in OEB_DOCS) if not names: return error_dialog(self.gui, _('No HTML files'), _('No HTML files are currently selected in the File browser'), show=True) with BusyCursor(): self.add_savepoint(_('Before HTML transformation')) try: changed = transform_container(current_container(), last_used_html_transform_rules, names) except: self.rewind_savepoint() raise if changed: self.apply_container_update_to_gui() if not changed: self.rewind_savepoint() return info_dialog(self.gui, _('No changes'), _('No HTML was changed.'), show=True) self.show_current_diff()

def transform_styles(self): global last_used_transform_rules if not self.ensure_book(_('You must first open a book in order to transform styles.')): return from calibre.ebooks.css_transform_rules import transform_container from calibre.gui2.css_transform_rules import RulesDialog d = RulesDialog(self.gui) d.rules = last_used_transform_rules ret = d.exec() last_used_transform_rules = d.rules if ret != QDialog.DialogCode.Accepted: return with BusyCursor(): self.add_savepoint(_('Before style transformation')) try: changed = transform_container(current_container(), last_used_transform_rules) except: self.rewind_savepoint() raise if changed: self.apply_container_update_to_gui() if not changed: self.rewind_savepoint() info_dialog(self.gui, _('No changes'), _( 'No styles were changed.'), show=True) return self.show_current_diff()

def get_external_resources(self): if not self.ensure_book(_('You must first open a book in order to transform styles.')): return from calibre.gui2.tweak_book.download import DownloadResources with BusyCursor(): self.add_savepoint(_('Before: Get external resources')) try: d = DownloadResources(self.gui) d.exec() except Exception: self.rewind_savepoint() raise if d.resources_replaced: self.apply_container_update_to_gui() if d.show_diff: self.show_current_diff() else: self.rewind_savepoint()

def manage_fonts(self): if not self.ensure_book(_('No book is currently open. You must first open a book to manage fonts.')): return self.commit_all_editors_to_container() self.gui.manage_fonts.display()

def manage_fonts_embed(self): self.polish('embed', _('Embed all fonts'), parent=self.gui.manage_fonts) self.gui.manage_fonts.refresh()

def manage_fonts_subset(self): self.polish('subset', _('Subset all fonts'), parent=self.gui.manage_fonts)

# Renaming {{{ def rationalize_folders(self): if not self.ensure_book(): return c = current_container() if not c.SUPPORTS_FILENAMES: return error_dialog(self.gui, _('Not supported'), _('The %s format does not support file and folder names internally, therefore' ' arranging files into folders is not allowed.') % c.book_type.upper(), show=True) d = RationalizeFolders(self.gui) if d.exec() != QDialog.DialogCode.Accepted: return self.commit_all_editors_to_container() name_map = rationalize_folders(c, d.folder_map) if not name_map: confirm(_( 'The files in this book are already arranged into folders'), 'already-arranged-into-folders', self.gui, pixmap='dialog_information.png', title=_('Nothing to do'), show_cancel_button=False, config_set=tprefs, confirm_msg=_('Show this message &again')) return self.add_savepoint(_('Before: Arrange into folders')) self.gui.blocking_job( 'rationalize_folders', _('Renaming and updating links...'), partial(self.rename_done, name_map), rename_files, current_container(), name_map)

def rename_requested(self, oldname, newname): self.commit_all_editors_to_container() if guess_type(oldname) != guess_type(newname): args = os.path.splitext(oldname) + os.path.splitext(newname) if not confirm( _('You are changing the file type of {0}<b>{1}</b> to {2}<b>{3}</b>.' ' Doing so can cause problems, are you sure?').format(*args), 'confirm-filetype-change', parent=self.gui, title=_('Are you sure?'), config_set=tprefs): return if urlnormalize(newname) != newname: if not confirm( _('The name you have chosen {0} contains special characters, internally' ' it will look like: {1}Try to use only the English alphabet [a-z], numbers [0-9],' ' hyphens and underscores for file names. Other characters can cause problems for ' ' different e-book viewers. Are you sure you want to proceed?').format( '<pre>%s</pre>'%newname, '<pre>%s</pre>' % urlnormalize(newname)), 'confirm-urlunsafe-change', parent=self.gui, title=_('Are you sure?'), config_set=tprefs): return self.add_savepoint(_('Before: Rename %s') % oldname) name_map = {oldname:newname} self.gui.blocking_job( 'rename_file', _('Renaming and updating links...'), partial(self.rename_done, name_map, from_filelist=self.gui.file_list.current_name), rename_files, current_container(), name_map)

def bulk_rename_requested(self, name_map): self.add_savepoint(_('Before: Bulk rename')) self.gui.blocking_job( 'bulk_rename_files', _('Renaming and updating links...'), partial(self.rename_done, name_map, from_filelist=self.gui.file_list.current_name), rename_files, current_container(), name_map)

def rename_done(self, name_map, job, from_filelist=None): if job.traceback is not None: self.gui.file_list.restore_temp_names() return error_dialog(self.gui, _('Failed to rename files'), _('Failed to rename files, click "Show details" for more information.'), det_msg=job.traceback, show=True) self.gui.file_list.build(current_container()) self.set_modified() for oldname, newname in iteritems(name_map): if oldname in editors: editors[newname] = ed = editors.pop(oldname) ed.change_document_name(newname) self.gui.central.rename_editor(editors[newname], newname) if self.gui.preview.current_name == oldname: self.gui.preview.current_name = newname self.apply_container_update_to_gui() if from_filelist: self.gui.file_list.select_names(frozenset(itervalues(name_map)), current_name=name_map.get(from_filelist)) self.gui.file_list.file_list.setFocus(Qt.FocusReason.PopupFocusReason)

# }}}

# Global history {{{ def do_global_undo(self, *a): container = self.global_undo.undo() if container is not None: set_current_container(container) self.apply_container_update_to_gui()

def do_global_redo(self): container = self.global_undo.redo() if container is not None: set_current_container(container) self.apply_container_update_to_gui()

def update_global_history_actions(self): gu = self.global_undo for x, text in (('undo', _('&Revert to')), ('redo', _('&Revert to'))): ac = getattr(self.gui, 'action_global_%s' % x) ac.setEnabled(getattr(gu, 'can_' + x)) ac.setText(text + ' "%s"'%(getattr(gu, x + '_msg') or '...'))

[belgeler] def add_savepoint(self, msg): ' Create a restore checkpoint with the name specified as ``msg`` ' self.commit_all_editors_to_container() nc = clone_container(current_container(), self.mkdtemp()) self.global_undo.add_savepoint(nc, msg) set_current_container(nc) self.update_global_history_actions()

[belgeler] def rewind_savepoint(self): ' Undo the previous creation of a restore checkpoint, useful if you create a checkpoint, then abort the operation with no changes ' container = self.global_undo.rewind_savepoint() if container is not None: set_current_container(container) self.update_global_history_actions()

def create_diff_dialog(self, revert_msg=_('&Revert changes'), show_open_in_editor=True): global _diff_dialogs from calibre.gui2.tweak_book.diff.main import Diff def line_activated(name, lnum, right): if right: self.edit_file_requested(name, None, guess_type(name)) if name in editors: editor = editors[name] editor.go_to_line(lnum) editor.setFocus(Qt.FocusReason.OtherFocusReason) self.gui.raise_and_focus() d = Diff(revert_button_msg=revert_msg, show_open_in_editor=show_open_in_editor) [x.break_cycles() for x in _diff_dialogs if not x.isVisible()] _diff_dialogs = [x for x in _diff_dialogs if x.isVisible()] + [d] d.show(), d.raise_and_focus(), d.setFocus(Qt.FocusReason.OtherFocusReason), d.setWindowModality(Qt.WindowModality.NonModal) if show_open_in_editor: d.line_activated.connect(line_activated) return d

[belgeler] def show_current_diff(self, allow_revert=True, to_container=None): ''' Show the changes to the book from its last checkpointed state

:param allow_revert: If True the diff dialog will have a button to allow the user to revert all changes :param to_container: A container object to compare the current container to. If None, the previously checkpointed container is used ''' self.commit_all_editors_to_container() k = {} if allow_revert else {'revert_msg': None} d = self.create_diff_dialog(**k) previous_container = self.global_undo.previous_container connect_lambda(d.revert_requested, self, lambda self: self.revert_requested(previous_container)) other = to_container or self.global_undo.previous_container d.container_diff(other, self.global_undo.current_container, names=(self.global_undo.label_for_container(other), self.global_undo.label_for_container(self.global_undo.current_container)))

def ask_to_show_current_diff(self, name, title, msg, allow_revert=True, to_container=None): if tprefs.get('skip_ask_to_show_current_diff_for_' + name): return d = QDialog(self.gui) k = QVBoxLayout(d) d.setWindowTitle(title) k.addWidget(QLabel(msg)) k.confirm = cb = QCheckBox(_('Show this popup again')) k.addWidget(cb) cb.setChecked(True) connect_lambda(cb.toggled, d, lambda d, checked: tprefs.set('skip_ask_to_show_current_diff_for_' + name, not checked)) d.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, d) k.addWidget(bb) bb.accepted.connect(d.accept) bb.rejected.connect(d.reject) d.b = b = bb.addButton(_('See what &changed'), QDialogButtonBox.ButtonRole.AcceptRole) b.setIcon(QIcon.ic('diff.png')), b.setAutoDefault(False) bb.button(QDialogButtonBox.StandardButton.Close).setDefault(True) if d.exec() == QDialog.DialogCode.Accepted: self.show_current_diff(allow_revert=allow_revert, to_container=to_container)

def compare_book(self): if not self.ensure_book(): return self.commit_all_editors_to_container() c = current_container() path = choose_files(self.gui, 'select-book-for-comparison', _('Choose book'), filters=[ (_('%s books') % c.book_type.upper(), (c.book_type,))], select_only_single_file=True, all_files=False) if path and path[0]: with TemporaryDirectory('_compare') as tdir: other = _gc(path[0], tdir=tdir, tweak_mode=True) d = self.create_diff_dialog(revert_msg=None) d.container_diff(other, c, names=(_('Other book'), _('Current book')))

def revert_requested(self, container): self.commit_all_editors_to_container() nc = self.global_undo.revert_to(container) set_current_container(nc) self.apply_container_update_to_gui()

def compare_requested(self, container): self.show_current_diff(to_container=container)

# }}}

[belgeler] def set_modified(self): ' Mark the book as having been modified ' self.gui.action_save.setEnabled(True)

def request_completion(self, name, completion_type, completion_data, query=None): if completion_type is None: completion_worker().clear_caches(completion_data) return request_id = (self.completion_request_count, name) self.completion_request_count += 1 completion_worker().queue_completion(request_id, completion_type, completion_data, query) return request_id[0]

def handle_completion_result(self, result): name = result.request_id[1] editor = editors.get(name) if editor is not None: editor.handle_completion_result(result)

def fix_html(self, current): if current: ed = self.gui.central.current_editor if hasattr(ed, 'fix_html'): ed.fix_html() else: with BusyCursor(): self.add_savepoint(_('Before: Fix HTML')) fix_all_html(current_container()) self.update_editors_from_container() self.set_modified() self.ask_to_show_current_diff('html-fix', _('Fixing done'), _('All HTML files fixed'))

def pretty_print(self, current): if current: ed = self.gui.central.current_editor ed.pretty_print(editor_name(ed)) else: with BusyCursor(): self.add_savepoint(_('Before: Beautify files')) pretty_all(current_container()) self.update_editors_from_container() self.set_modified() QApplication.alert(self.gui) self.ask_to_show_current_diff('beautify', _('Beautified'), _('All files beautified'))

def mark_selected_text(self): ed = self.gui.central.current_editor if ed is not None: ed.mark_selected_text() if ed.has_marked_text: self.gui.central.search_panel.set_where('selected-text') else: self.gui.central.search_panel.unset_marked()

def editor_action(self, action): ed = self.gui.central.current_editor edname = editor_name(ed) if hasattr(ed, 'action_triggered'): if action and action[0] == 'insert_resource': rtype = action[1] if rtype == 'image' and ed.syntax not in {'css', 'html'}: return error_dialog(self.gui, _('Not supported'), _( 'Inserting images is only supported for HTML and CSS files.'), show=True) rdata = get_resource_data(rtype, self.gui) if rdata is None: return if rtype == 'image': chosen_name, chosen_image_is_external, fullpage, preserve_ar = rdata if chosen_image_is_external: with open(chosen_image_is_external[1], 'rb') as f: current_container().add_file(chosen_image_is_external[0], f.read()) self.refresh_file_list() chosen_name = chosen_image_is_external[0] href = current_container().name_to_href(chosen_name, edname) fmt, width, height = identify(current_container().raw_data(chosen_name, decode=False)) ed.insert_image(href, fullpage=fullpage, preserve_aspect_ratio=preserve_ar, width=width, height=height) elif action[0] == 'insert_hyperlink': self.commit_all_editors_to_container() d = InsertLink(current_container(), edname, initial_text=ed.get_smart_selection(), parent=self.gui) if d.exec() == QDialog.DialogCode.Accepted: ed.insert_hyperlink(d.href, d.text, template=d.rendered_template) elif action[0] == 'insert_tag': d = InsertTag(parent=self.gui) if d.exec() == QDialog.DialogCode.Accepted: ed.insert_tag(d.tag) else: ed.action_triggered(action)

def rename_class(self, class_name): self.commit_all_editors_to_container() text, ok = QInputDialog.getText(self.gui, _('New class name'), _( 'Rename the class {} to?').format(class_name)) if ok: self.add_savepoint(_('Before: Rename {}').format(class_name)) with BusyCursor(): changed = rename_class(current_container(), class_name, text.strip()) if changed: self.apply_container_update_to_gui() self.show_current_diff() else: self.rewind_savepoint() return info_dialog(self.gui, _('No matches'), _( 'No class {} found to change').format(class_name), show=True)

def set_semantics(self): if not self.ensure_book(): return self.commit_all_editors_to_container() c = current_container() if c.book_type == 'azw3': return error_dialog(self.gui, _('Not supported'), _( 'Semantics are not supported for the AZW3 format.'), show=True) d = InsertSemantics(c, parent=self.gui) if d.exec() == QDialog.DialogCode.Accepted and d.changes: self.add_savepoint(_('Before: Set Semantics')) d.apply_changes(current_container()) self.apply_container_update_to_gui()

def filter_css(self): self.commit_all_editors_to_container() c = current_container() ed = self.gui.central.current_editor current_name = editor_name(ed) if current_name and c.mime_map[current_name] not in OEB_DOCS | OEB_STYLES: current_name = None d = FilterCSS(current_name=current_name, parent=self.gui) if d.exec() == QDialog.DialogCode.Accepted and d.filtered_properties: self.add_savepoint(_('Before: Filter style information')) with BusyCursor(): changed = filter_css(current_container(), d.filtered_properties, names=d.filter_names) if changed: self.apply_container_update_to_gui() self.show_current_diff() else: self.rewind_savepoint() return info_dialog(self.gui, _('No matches'), _( 'No matching style rules were found'), show=True)

def show_find(self): self.gui.central.show_find() ed = self.gui.central.current_editor if ed is not None and hasattr(ed, 'selected_text'): text = ed.selected_text if text and text.strip(): self.gui.central.pre_fill_search(text)

def show_text_search(self): self.gui.text_search_dock.show() self.gui.text_search.find.setFocus(Qt.FocusReason.OtherFocusReason)

def search_action_triggered(self, action, overrides=None): ss = self.gui.saved_searches.isVisible() trigger_saved_search = ss and (not self.gui.central.search_panel.isVisible() or self.gui.saved_searches.has_focus()) if trigger_saved_search: return self.gui.saved_searches.trigger_action(action, overrides=overrides) self.search(action, overrides)

def run_saved_searches(self, searches, action): ed = self.gui.central.current_editor name = editor_name(ed) searchable_names = self.gui.file_list.searchable_names if not searches or not validate_search_request(name, searchable_names, getattr(ed, 'has_marked_text', False), searches[0], self.gui): return ret = run_search(searches, action, ed, name, searchable_names, self.gui, self.show_editor, self.edit_file, self.show_current_diff, self.add_savepoint, self.rewind_savepoint, self.set_modified) ed = ret is True and self.gui.central.current_editor if getattr(ed, 'has_line_numbers', False): ed.editor.setFocus(Qt.FocusReason.OtherFocusReason) else: self.gui.saved_searches.setFocus(Qt.FocusReason.OtherFocusReason)

def search(self, action, overrides=None): # Run a search/replace sp = self.gui.central.search_panel # Ensure the search panel is visible sp.setVisible(True) ed = self.gui.central.current_editor name = editor_name(ed) state = sp.state if overrides: state.update(overrides) searchable_names = self.gui.file_list.searchable_names if not validate_search_request(name, searchable_names, getattr(ed, 'has_marked_text', False), state, self.gui): return ret = run_search(state, action, ed, name, searchable_names, self.gui, self.show_editor, self.edit_file, self.show_current_diff, self.add_savepoint, self.rewind_savepoint, self.set_modified) ed = ret is True and self.gui.central.current_editor if getattr(ed, 'has_line_numbers', False): ed.editor.setFocus(Qt.FocusReason.OtherFocusReason) else: self.gui.saved_searches.setFocus(Qt.FocusReason.OtherFocusReason)

def find_text(self, state): from calibre.gui2.tweak_book.text_search import run_text_search searchable_names = self.gui.file_list.searchable_names ed = self.gui.central.current_editor name = editor_name(ed) if not validate_search_request(name, searchable_names, getattr(ed, 'has_marked_text', False), state, self.gui): return ret = run_text_search(state, ed, name, searchable_names, self.gui, self.show_editor, self.edit_file) ed = ret is True and self.gui.central.current_editor if getattr(ed, 'has_line_numbers', False): ed.editor.setFocus(Qt.FocusReason.OtherFocusReason)

def find_initial_text(self, text): from calibre.gui2.tweak_book.search import get_search_regex from calibre.gui2.tweak_book.text_search import file_matches_pattern search = {'find': text, 'mode': 'normal', 'case_sensitive': True, 'direction': 'down'} pat = get_search_regex(search) searchable_names = set(self.gui.file_list.searchable_names['text']) for name, ed in iteritems(editors): searchable_names.discard(name) if ed.find_text(pat, complete=True): self.show_editor(name) return for name in searchable_names: if file_matches_pattern(name, pat): self.edit_file(name) if editors[name].find_text(pat, complete=True): return def find_word(self, word, locations): # Go to a word from the spell check dialog ed = self.gui.central.current_editor name = editor_name(ed) find_next_word(word, locations, ed, name, self.gui, self.show_editor, self.edit_file)

def next_spell_error(self): # Go to the next spelling error ed = self.gui.central.current_editor name = editor_name(ed) find_next_error(ed, name, self.gui, self.show_editor, self.edit_file, self.close_editor)

def word_change_requested(self, w, new_word): if self.commit_all_editors_to_container(): self.gui.spell_check.change_word_after_update(w, new_word) else: self.gui.spell_check.do_change_word(w, new_word)

def word_replaced(self, changed_names): self.set_modified() self.update_editors_from_container(names=set(changed_names))

def word_ignored(self, word, locale): if tprefs['inline_spell_check']: for ed in itervalues(editors): try: ed.editor.recheck_word(word, locale) except AttributeError: pass def editor_link_clicked(self, url): ed = self.gui.central.current_editor name = editor_name(ed) if url.startswith('#'): target = name else: target = current_container().href_to_name(url, name) frag = url.partition('#')[-1] if current_container().has_name(target): self.link_clicked(target, frag, show_anchor_not_found=True) else: try: purl = urlparse(url) except ValueError: return if purl.scheme not in {'', 'file'}: open_url(QUrl(url)) else: error_dialog(self.gui, _('Not found'), _( 'No file with the name %s was found in the book') % target, show=True)

def editor_class_clicked(self, class_data): from calibre.gui2.tweak_book.jump_to_class import NoMatchingRuleFound, NoMatchingTagFound, find_first_matching_rule ed = self.gui.central.current_editor name = editor_name(ed) try: res = find_first_matching_rule(current_container(), name, ed.get_raw_data(), class_data) except (NoMatchingTagFound, NoMatchingRuleFound): res = None if res is not None and res.file_name and res.rule_address: editor = self.open_editor_for_name(res.file_name) if editor: editor.goto_css_rule(res.rule_address, sourceline_address=res.style_tag_address) else: error_dialog(self.gui, _('No matches found'), _('No style rules that match the class {} were found').format( class_data['class']), show=True)

def save_search(self): state = self.gui.central.search_panel.state self.show_saved_searches() self.gui.saved_searches.add_predefined_search(state)

def show_saved_searches(self): self.gui.saved_searches_dock.show() saved_searches = show_saved_searches def create_checkpoint(self): text, ok = QInputDialog.getText(self.gui, _('Choose name'), _( 'Choose a name for the checkpoint.\nYou can later restore the book' ' to this checkpoint via the\n"Revert to..." entries in the Edit menu.')) if ok: self.add_savepoint(text)

def commit_editor_to_container(self, name, container=None): container = container or current_container() ed = editors[name] with container.open(name, 'wb') as f: f.write(ed.data) if name == container.opf_name: container.refresh_mime_map() lang = container.opf_xpath('//dc:language/text()') or [self.current_metadata.language] set_book_locale(lang[0]) if container is current_container(): ed.is_synced_to_container = True if name == container.opf_name: self.gui.file_list.build(container)

[belgeler] def commit_all_editors_to_container(self): ''' Commit any changes that the user has made to files open in editors to the container. You should call this method before performing any actions on the current container ''' changed = False with BusyCursor(): for name, ed in iteritems(editors): if not ed.is_synced_to_container: self.commit_editor_to_container(name) ed.is_synced_to_container = True changed = True return changed

[belgeler] def save_book(self): ' Save the book. Saving is performed in the background ' self.gui.update_window_title() c = current_container() for name, ed in iteritems(editors): if ed.is_modified or not ed.is_synced_to_container: self.commit_editor_to_container(name, c) ed.is_modified = False path_to_ebook = os.path.abspath(c.path_to_ebook) destdir = os.path.dirname(path_to_ebook) if not c.is_dir and not os.path.exists(destdir): info_dialog(self.gui, _('Path does not exist'), _( 'The file you are editing (%s) no longer exists. You have to choose a new save location.') % path_to_ebook, show_copy_button=False, show=True) fmt = path_to_ebook.rpartition('.')[-1].lower() start_dir = find_first_existing_ancestor(path_to_ebook) path = choose_save_file( self.gui, 'choose-new-save-location', _('Choose file location'), initial_path=os.path.join(start_dir, os.path.basename(path_to_ebook)), filters=[(fmt.upper(), (fmt,))], all_files=False) if path is not None: if not path.lower().endswith('.' + fmt): path = path + '.' + fmt path = os.path.abspath(path) c.path_to_ebook = path self.global_undo.update_path_to_ebook(path) else: return if os.path.exists(c.path_to_ebook) and not os.access(c.path_to_ebook, os.W_OK): if not question_dialog(self.gui, _('File is read-only'), _( 'The file at {} is read-only. The editor will try to reset its permissions before saving. Proceed with saving?' ).format(c.path_to_ebook), override_icon='dialog_warning.png', yes_text=_('&Save'), no_text=_('&Cancel'), yes_icon='save.png'): return self.gui.action_save.setEnabled(False) tdir = self.mkdtemp(prefix='save-') container = clone_container(c, tdir) self.save_manager.schedule(tdir, container)def _save_copy(self, post_action=None): self.gui.update_window_title() c = current_container() if c.is_dir: return error_dialog(self.gui, _('Cannot save a copy'), _( 'Saving a copy of a folder based book is not supported'), show=True) ext = c.path_to_ebook.rpartition('.')[-1] path = choose_save_file( self.gui, 'tweak_book_save_copy', _('Choose path'), initial_filename=self.current_metadata.title + '.' + ext, filters=[(_('Book (%s)') % ext.upper(), [ext.lower()])], all_files=False) if not path: return if '.' not in os.path.basename(path): path += '.' + ext.lower() tdir = self.mkdtemp(prefix='save-copy-') container = clone_container(c, tdir) for name, ed in iteritems(editors): if ed.is_modified or not ed.is_synced_to_container: self.commit_editor_to_container(name, container)

def do_save(c, path, tdir): save_container(c, path) shutil.rmtree(tdir, ignore_errors=True) return path self.gui.blocking_job('save_copy', _('Saving copy, please wait...'), partial(self.copy_saved, post_action=post_action), do_save, container, path, tdir)

def save_copy(self): self._save_copy()

def copy_saved(self, job, post_action=None): if job.traceback is not None: return error_dialog(self.gui, _('Failed to save copy'), _('Failed to save copy, click "Show details" for more information.'), det_msg=job.traceback, show=True) msg = _('Copy saved to %s') % job.result if post_action is None: info_dialog(self.gui, _('Copy saved'), msg, show=True) elif post_action == 'replace': msg = _('Editing saved copy at: %s') % job.result self.open_book(job.result, edit_file=self.currently_editing) elif post_action == 'edit': if ismacos: cmd = ['open', '-F', '-n', '-a', os.path.dirname(os.path.dirname(os.path.dirname(macos_edit_book_bundle_path()))), '--args'] else: cmd = [exe_path('ebook-edit')] if islinux: cmd.append('--detach') cmd.append(job.result) ce = self.currently_editing if ce: cmd.append(ce) with sanitize_env_vars(): subprocess.Popen(cmd) self.gui.show_status_message(msg, 5)

def report_save_error(self, tb): if self.doing_terminal_save: prints(tb, file=sys.stderr) self.abort_terminal_save() self.set_modified() error_dialog(self.gui, _('Could not save'), _('Saving of the book failed. Click "Show details"' ' for more information. You can try to save a copy' ' to a different location, via File->Save a copy'), det_msg=tb, show=True)

def go_to_line_number(self): ed = self.gui.central.current_editor if ed is None or not ed.has_line_numbers: return num, ok = QInputDialog.getInt(self.gui, _('Enter line number'), ('Line number:'), ed.current_line, 1, max(100000, ed.number_of_lines)) if ok: ed.current_line = num def split_start_requested(self): self.commit_all_editors_to_container() self.gui.preview.do_start_split()

@in_thread_job def split_requested(self, name, loc, totals): self.add_savepoint(_('Before: Split %s') % self.gui.elided_text(name)) try: bottom_name = split(current_container(), name, loc, totals=totals) except AbortError: self.rewind_savepoint() raise self.apply_container_update_to_gui() self.edit_file(bottom_name, 'html')

def multisplit(self): ed = self.gui.central.current_editor if ed.syntax != 'html': return name = editor_name(ed) if name is None: return d = MultiSplit(self.gui) if d.exec() == QDialog.DialogCode.Accepted: with BusyCursor(): self.add_savepoint(_('Before: Split %s') % self.gui.elided_text(name)) try: multisplit(current_container(), name, d.xpath) except AbortError: self.rewind_savepoint() raise self.apply_container_update_to_gui()

def open_editor_for_name(self, name): if name in editors: editor = editors[name] self.gui.central.show_editor(editor) else: try: mt = current_container().mime_map[name] except KeyError: error_dialog(self.gui, _('Does not exist'), _( 'The file %s does not exist. If you were trying to click an item in' ' the Table of Contents, you may' ' need to refresh it by right-clicking and choosing "Refresh".') % name, show=True) return None syntax = syntax_from_mime(name, mt) if not syntax: error_dialog( self.gui, _('Unsupported file format'), _('Editing files of type %s is not supported') % mt, show=True) return None editor = self.edit_file(name, syntax) return editor def link_clicked(self, name, anchor, show_anchor_not_found=False): if not name: return editor = self.open_editor_for_name(name) if anchor and hasattr(editor, 'go_to_anchor') : if editor.go_to_anchor(anchor): self.gui.preview.pending_go_to_anchor = anchor elif show_anchor_not_found: error_dialog(self.gui, _('Not found'), _( 'The anchor %s was not found in this file') % anchor, show=True)

@in_thread_job def check_item_activated(self, item): is_mult = item.has_multiple_locations and getattr(item, 'current_location_index', None) is not None name = item.all_locations[item.current_location_index][0] if is_mult else item.name editor = None if name in editors: editor = editors[name] self.gui.central.show_editor(editor) else: try: editor = self.edit_file_requested(name, None, current_container().mime_map[name]) except KeyError: error_dialog(self.gui, _('File deleted'), _( 'The file {} has already been deleted, re-run Check Book to update the results.').format(name), show=True) if getattr(editor, 'has_line_numbers', False): if is_mult: editor.go_to_line(*(item.all_locations[item.current_location_index][1:3])) else: editor.go_to_line(item.line or 0, item.col or 0) editor.set_focus()

@in_thread_job def check_requested(self, *args): if current_container() is None: return self.commit_all_editors_to_container() c = self.gui.check_book c.parent().show() c.parent().raise_and_focus() c.run_checks(current_container())

def spell_check_requested(self): if current_container() is None: return self.commit_all_editors_to_container() self.add_savepoint(_('Before: Spell Check')) self.gui.spell_check.show()

@in_thread_job def fix_requested(self, errors): self.add_savepoint(_('Before: Auto-fix errors')) c = self.gui.check_book c.parent().show() c.parent().raise_and_focus() changed = c.fix_errors(current_container(), errors) if changed: self.apply_container_update_to_gui() self.set_modified() else: self.rewind_savepoint()

@in_thread_job def merge_requested(self, category, names, master): self.add_savepoint(_('Before: Merge files into %s') % self.gui.elided_text(master)) try: merge(current_container(), category, names, master) except AbortError: self.rewind_savepoint() raise self.apply_container_update_to_gui() if master in editors: self.show_editor(master) self.gui.file_list.merge_completed(master) self.gui.message_popup(_('{} files merged').format(len(names)))

@in_thread_job def link_stylesheets_requested(self, names, sheets, remove): self.add_savepoint(_('Before: Link stylesheets')) changed_names = link_stylesheets(current_container(), names, sheets, remove) if changed_names: self.update_editors_from_container(names=changed_names) self.set_modified()

@in_thread_job def export_requested(self, name_or_names, path): if isinstance(name_or_names, string_or_bytes): return self.export_file(name_or_names, path) for name in name_or_names: dest = os.path.abspath(os.path.join(path, name)) if '/' in name or os.sep in name: try: os.makedirs(os.path.dirname(dest)) except OSError as err: if err.errno != errno.EEXIST: raise self.export_file(name, dest)

def open_file_with(self, file_name, fmt, entry): if file_name in editors and not editors[file_name].is_synced_to_container: self.commit_editor_to_container(file_name) with current_container().open(file_name) as src: tdir = PersistentTemporaryDirectory(suffix='-ee-ow') with open(os.path.join(tdir, os.path.basename(file_name)), 'wb') as dest: shutil.copyfileobj(src, dest) from calibre.gui2.open_with import run_program run_program(entry, dest.name, self) if question_dialog(self.gui, _('File opened'), _( 'When you are done editing {0} click "Import" to update' ' the file in the book or "Discard" to lose any changes.').format(file_name), yes_text=_('Import'), no_text=_('Discard') ): self.add_savepoint(_('Before: Replace %s') % file_name) with open(dest.name, 'rb') as src, current_container().open(file_name, 'wb') as cdest: shutil.copyfileobj(src, cdest) self.apply_container_update_to_gui() try: shutil.rmtree(tdir) except Exception: pass

@in_thread_job def copy_files_to_clipboard(self, names): names = tuple(names) for name in names: if name in editors and not editors[name].is_synced_to_container: self.commit_editor_to_container(name) container = current_container() md = QMimeData() url_map = { name:container.get_file_path_for_processing(name, allow_modification=False) for name in names } md.setUrls(list(map(QUrl.fromLocalFile, list(url_map.values())))) import json md.setData(FILE_COPY_MIME, as_bytes(json.dumps({ name: (url_map[name], container.mime_map.get(name)) for name in names }))) QApplication.instance().clipboard().setMimeData(md)

@in_thread_job def paste_files_from_clipboard(self): md = QApplication.instance().clipboard().mimeData() if md.hasUrls() and md.hasFormat(FILE_COPY_MIME): import json self.commit_all_editors_to_container() name_map = json.loads(bytes(md.data(FILE_COPY_MIME))) container = current_container() for name, (path, mt) in iteritems(name_map): with open(path, 'rb') as f: container.add_file(name, f.read(), media_type=mt, modify_name_if_needed=True) self.apply_container_update_to_gui()

def export_file(self, name, path): if name in editors and not editors[name].is_synced_to_container: self.commit_editor_to_container(name) with current_container().open(name, 'rb') as src, open(path, 'wb') as dest: shutil.copyfileobj(src, dest)

@in_thread_job def replace_requested(self, name, path, basename, force_mt): self.add_savepoint(_('Before: Replace %s') % name) replace_file(current_container(), name, path, basename, force_mt) self.apply_container_update_to_gui()

def browse_images(self): self.gui.image_browser.refresh() self.gui.image_browser.show() self.gui.image_browser.raise_and_focus()

def show_reports(self): if not self.ensure_book(_('You must first open a book in order to see the report.')): return self.gui.reports.refresh() self.gui.reports.show() self.gui.reports.raise_and_focus()

def reports_edit_requested(self, name): mt = current_container().mime_map.get(name, guess_type(name)) self.edit_file_requested(name, None, mt)

def image_activated(self, name): mt = current_container().mime_map.get(name, guess_type(name)) self.edit_file_requested(name, None, mt)

def check_external_links(self): if self.ensure_book(_('You must first open a book in order to check links.')): self.commit_all_editors_to_container() self.gui.check_external_links.show()

def compress_images(self): if not self.ensure_book(_('You must first open a book in order to compress images.')): return from calibre.gui2.tweak_book.polish import CompressImages, CompressImagesProgress, show_report d = CompressImages(self.gui) if d.exec() == QDialog.DialogCode.Accepted: with BusyCursor(): self.add_savepoint(_('Before: compress images')) d = CompressImagesProgress(names=d.names, jpeg_quality=d.jpeg_quality, webp_quality=d.webp_quality, parent=self.gui) if d.exec() != QDialog.DialogCode.Accepted: self.rewind_savepoint() return changed, report = d.result if changed is None and report: self.rewind_savepoint() return error_dialog(self.gui, _('Unexpected error'), _( 'Failed to compress images, click "Show details" for more information'), det_msg=report, show=True) if changed: self.apply_container_update_to_gui() else: self.rewind_savepoint() show_report(changed, self.current_metadata.title, report, self.gui, self.show_current_diff)

def sync_editor_to_preview(self, name, sourceline_address): editor = self.edit_file(name, 'html') self.ignore_preview_to_editor_sync = True try: editor.goto_sourceline(*sourceline_address) finally: self.ignore_preview_to_editor_sync = False def do_sync_preview_to_editor(self, wait_for_highlight_to_finish=False): if self.ignore_preview_to_editor_sync: return ed = self.gui.central.current_editor if ed is not None: name = editor_name(ed) if name is not None and getattr(ed, 'syntax', None) == 'html': hl = getattr(ed, 'highlighter', None) if wait_for_highlight_to_finish: if getattr(hl, 'is_working', False): QTimer.singleShot(75, self.sync_preview_to_editor_on_highlight_finish) return ct = ed.current_tag() self.gui.preview.sync_to_editor(name, ct) if hl is not None and hl.is_working: QTimer.singleShot(75, self.sync_preview_to_editor_on_highlight_finish)

[belgeler] def sync_preview_to_editor(self): ' Sync the position of the preview panel to the current cursor position in the current editor ' self.do_sync_preview_to_editor()

def sync_preview_to_editor_on_highlight_finish(self): self.do_sync_preview_to_editor(wait_for_highlight_to_finish=True)

def show_partial_cfi_in_editor(self, name, cfi): editor = self.edit_file(name, 'html') if not editor or not editor.has_line_numbers: return False from calibre.ebooks.epub.cfi.parse import decode_cfi from calibre.ebooks.oeb.polish.parsing import parse root = parse( editor.get_raw_data(), decoder=lambda x: x.decode('utf-8'), line_numbers=True, linenumber_attribute='data-lnum') node = decode_cfi(root, cfi)

def barename(x): return x.tag.partition('}')[-1]

if node is not None: lnum = node.get('data-lnum') if lnum: tags_before = [] for tag in root.xpath('//*[@data-lnum="%s"]' % lnum): tags_before.append(barename(tag)) if tag is node: break else: tags_before.append(barename(node)) lnum = int(lnum) return editor.goto_sourceline(lnum, tags_before, attribute='id' if node.get('id') else None) return False def goto_style_declaration(self, data): name = data['name'] editor = self.edit_file(name, syntax=data['syntax']) self.gui.live_css.navigate_to_declaration(data, editor)

def init_editor(self, name, editor, data=None, use_template=False): editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed) editor.data_changed.connect(self.editor_data_changed) editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed) editor.cursor_position_changed.connect(self.sync_preview_to_editor) editor.cursor_position_changed.connect(self.update_cursor_position) if hasattr(editor, 'word_ignored'): editor.word_ignored.connect(self.word_ignored) if hasattr(editor, 'link_clicked'): editor.link_clicked.connect(self.editor_link_clicked) if hasattr(editor, 'class_clicked'): editor.class_clicked.connect(self.editor_class_clicked) if hasattr(editor, 'rename_class'): editor.rename_class.connect(self.rename_class) if getattr(editor, 'syntax', None) == 'html': editor.smart_highlighting_updated.connect(self.gui.live_css.sync_to_editor) if hasattr(editor, 'set_request_completion'): editor.set_request_completion(partial(self.request_completion, name), name) if data is not None: if use_template: editor.init_from_template(data) else: editor.data = data editor.is_synced_to_container = True editor.modification_state_changed.connect(self.editor_modification_state_changed) self.gui.central.add_editor(name, editor)

[belgeler] def edit_file(self, name, syntax=None, use_template=None): ''' Open the file specified by name in an editor

:param syntax: The media type of the file, for example, ``'text/html'``. If not specified it is guessed from the file extension. :param use_template: A template to initialize the opened editor with ''' editor = editors.get(name, None) if editor is None: syntax = syntax or syntax_from_mime(name, guess_type(name)) if use_template is None: data = current_container().raw_data(name) if isbytestring(data) and syntax in {'html', 'css', 'text', 'xml', 'javascript'}: try: data = data.decode('utf-8') except UnicodeDecodeError: return error_dialog(self.gui, _('Cannot decode'), _( 'Cannot edit %s as it appears to be in an unknown character encoding') % name, show=True) else: data = use_template editor = editors[name] = editor_from_syntax(syntax, self.gui.editor_tabs) self.init_editor(name, editor, data, use_template=bool(use_template)) if tprefs['pretty_print_on_open']: editor.pretty_print(name) self.show_editor(name) return editor

[belgeler] def show_editor(self, name): ' Show the editor that is editing the file specified by ``name`` ' self.gui.central.show_editor(editors[name]) editors[name].set_focus()

def edit_file_requested(self, name, syntax=None, mime=None): if name in editors: self.gui.central.show_editor(editors[name]) return editors[name] mime = mime or current_container().mime_map.get(name, guess_type(name)) syntax = syntax or syntax_from_mime(name, mime) if not syntax: return error_dialog( self.gui, _('Unsupported file format'), _('Editing files of type %s is not supported') % mime, show=True) return self.edit_file(name, syntax)

def edit_next_file(self, backwards=False): self.gui.file_list.edit_next_file(self.currently_editing, backwards)

def quick_open(self): if not self.ensure_book(_('No book is currently open. You must first open a book to edit.')): return c = current_container() files = [name for name, mime in iteritems(c.mime_map) if c.exists(name) and syntax_from_mime(name, mime) is not None] d = QuickOpen(files, parent=self.gui) if d.exec() == QDialog.DialogCode.Accepted and d.selected_result is not None: self.edit_file_requested(d.selected_result, None, c.mime_map[d.selected_result])

# Editor basic controls {{{ def do_editor_undo(self): ed = self.gui.central.current_editor if ed is not None: ed.undo()

def do_editor_redo(self): ed = self.gui.central.current_editor if ed is not None: ed.redo()

def do_editor_copy(self): ed = self.gui.central.current_editor if ed is not None: ed.copy()

def do_editor_cut(self): ed = self.gui.central.current_editor if ed is not None: ed.cut()

def do_editor_paste(self): ed = self.gui.central.current_editor if ed is not None: ed.paste()

def editor_data_changed(self, editor): self.gui.preview.start_refresh_timer() for name, ed in iteritems(editors): if ed is editor: self.gui.toc_view.start_refresh_timer(name) break def editor_undo_redo_state_changed(self, *args): self.apply_current_editor_state()

def editor_copy_available_state_changed(self, *args): self.apply_current_editor_state()

def editor_modification_state_changed(self, is_modified): self.apply_current_editor_state() if is_modified: self.set_modified() # }}}

def apply_current_editor_state(self): ed = self.gui.central.current_editor self.gui.cursor_position_widget.update_position() if ed is not None: actions['editor-undo'].setEnabled(ed.undo_available) actions['editor-redo'].setEnabled(ed.redo_available) actions['editor-copy'].setEnabled(ed.copy_available) actions['editor-cut'].setEnabled(ed.cut_available) actions['go-to-line-number'].setEnabled(ed.has_line_numbers) actions['fix-html-current'].setEnabled(ed.syntax == 'html') name = editor_name(ed) mime = current_container().mime_map.get(name) if name is not None and (getattr(ed, 'syntax', None) == 'html' or mime == 'image/svg+xml'): if self.gui.preview.show(name): # The file being displayed by the preview has changed. # Set the preview's position to the current cursor # position in the editor, in case the editors' cursor # position has not changed, since the last time it was # focused. This is not inefficient since multiple requests # to sync are de-bounced with a 100 msec wait. self.sync_preview_to_editor() if name is not None: self.gui.file_list.mark_name_as_current(name) if ed.has_line_numbers: self.gui.cursor_position_widget.update_position(*ed.cursor_position) else: actions['go-to-line-number'].setEnabled(False) self.gui.file_list.clear_currently_edited_name()

def update_cursor_position(self): ed = self.gui.central.current_editor if getattr(ed, 'has_line_numbers', False): self.gui.cursor_position_widget.update_position(*ed.cursor_position) else: self.gui.cursor_position_widget.update_position()

def editor_close_requested(self, editor): name = editor_name(editor) if not name: return if not editor.is_synced_to_container: self.commit_editor_to_container(name) self.close_editor(name)

[belgeler] def close_editor(self, name): ' Close the editor that is editing the file specified by ``name`` ' editor = editors.pop(name) self.gui.central.close_editor(editor) editor.break_cycles() if not editors or getattr(self.gui.central.current_editor, 'syntax', None) != 'html': self.gui.preview.clear() self.gui.live_css.clear()

def insert_character(self): self.gui.insert_char.show()

def manage_snippets(self): from calibre.gui2.tweak_book.editor.snippets import UserSnippets UserSnippets(self.gui).exec()

def merge_files(self): self.gui.file_list.merge_files()

# Shutdown {{{ def quit(self): if self.doing_terminal_save: return False if self.save_manager.has_tasks: if question_dialog( self.gui, _('Are you sure?'), _( 'The current book is being saved in the background. Quitting now will' ' <b>abort the save process</b>! Finish saving first?'), yes_text=_('Finish &saving first'), no_text=_('&Quit immediately')): if self.save_manager.has_tasks: self.start_terminal_save_indicator() return False if not self.confirm_quit(): return False self.shutdown() QApplication.instance().exit() return True def confirm_quit(self): if self.gui.action_save.isEnabled(): d = QDialog(self.gui) d.l = QGridLayout(d) d.setLayout(d.l) d.setWindowTitle(_('Unsaved changes')) d.i = QLabel('') d.i.setMaximumSize(QSize(64, 64)) d.i.setPixmap(QIcon.ic('dialog_warning.png').pixmap(d.i.maximumSize())) d.l.addWidget(d.i, 0, 0) d.m = QLabel(_('There are unsaved changes, if you quit without saving, you will lose them.')) d.m.setWordWrap(True) d.l.addWidget(d.m, 0, 1) d.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Cancel) d.bb.rejected.connect(d.reject) d.bb.accepted.connect(d.accept) d.l.addWidget(d.bb, 1, 0, 1, 2) d.do_save = None def endit(d, x): d.do_save = x d.accept() b = d.bb.addButton(_('&Save and Quit'), QDialogButtonBox.ButtonRole.ActionRole) b.setIcon(QIcon.ic('save.png')) connect_lambda(b.clicked, d, lambda d: endit(d, True)) b = d.bb.addButton(_('&Quit without saving'), QDialogButtonBox.ButtonRole.ActionRole) connect_lambda(b.clicked, d, lambda d: endit(d, False)) d.resize(d.sizeHint()) if d.exec() != QDialog.DialogCode.Accepted or d.do_save is None: return False if d.do_save: self.gui.action_save.trigger() self.start_terminal_save_indicator() return False return True def start_terminal_save_indicator(self): self.save_state() self.gui.blocking_job.set_msg(_('Saving, please wait...')) self.gui.blocking_job.start() self.doing_terminal_save = True def abort_terminal_save(self): self.doing_terminal_save = False self.gui.blocking_job.stop()

def check_terminal_save(self): if self.doing_terminal_save and not self.save_manager.has_tasks: # terminal save could have been aborted self.shutdown() QApplication.instance().exit()

def shutdown(self): self.save_state() completion_worker().shutdown() self.save_manager.check_for_completion.disconnect() self.gui.preview.stop_refresh_timer() self.gui.live_css.stop_update_timer() [x.reject() for x in _diff_dialogs] del _diff_dialogs[:] self.save_manager.shutdown() parse_worker.shutdown() self.save_manager.wait(0.1)

def save_state(self): with self.editor_cache: self.save_book_edit_state() with tprefs: self.gui.save_state()

def save_book_edit_state(self): c = current_container() if c and c.path_to_ebook: tprefs = self.editor_cache mem = tprefs['edit_book_state'] order = tprefs['edit_book_state_order'] extra = len(order) - 99 if extra > 0: order = [k for k in order[extra:] if k in mem] mem = {k:mem[k] for k in order} mem[c.path_to_ebook] = { 'editors':{name:ed.current_editing_state for name, ed in iteritems(editors)}, 'currently_editing':self.currently_editing, 'tab_order':self.gui.central.tab_order, } try: order.remove(c.path_to_ebook) except ValueError: pass order.append(c.path_to_ebook) tprefs['edit_book_state'] = mem tprefs['edit_book_state_order'] = order def restore_book_edit_state(self): c = current_container() if c and c.path_to_ebook: tprefs = self.editor_cache state = tprefs['edit_book_state'].get(c.path_to_ebook) if state is not None: opened = set() eds = state.get('editors', {}) for name in state.get('tab_order', ()): if c.has_name(name): try: editor = self.edit_file_requested(name) if editor is not None: opened.add(name) es = eds.get(name) if es is not None: editor.current_editing_state = es except Exception: import traceback traceback.print_exc() ce = state.get('currently_editing') if ce in opened: self.show_editor(ce)# }}}

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.gui2.tweak_book.plugin

calibre.gui2.tweak_book.plugin öğesinin kaynak kodu

#!/usr/bin/env python

__license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'

import importlib import sys from qt.core import QToolButton from calibre import prints from calibre.customize import PluginInstallationType from calibre.customize.ui import all_edit_book_tool_plugins from calibre.gui2.tweak_book import current_container, tprefs from calibre.gui2.tweak_book.boss import get_boss from polyglot.builtins import itervalues

[belgeler] class Tool:

''' The base class for individual tools in an Edit Book plugin. Useful members include:

* ``self.plugin``: A reference to the :class:`calibre.customize.Plugin` object to which this tool belongs. * self. :attr:`boss` * self. :attr:`gui`

Methods that must be overridden in sub classes:

* :meth:`create_action` * :meth:`register_shortcut`

'''

#: Set this to a unique name it will be used as a key name = None

#: If True the user can choose to place this tool in the plugins toolbar allowed_in_toolbar = True

#: If True the user can choose to place this tool in the plugins menu allowed_in_menu = True

#: The popup mode for the menu (if any) of the toolbar button. Possible values are 'delayed', 'instant', 'button' toolbar_button_popup_mode = 'delayed'

@property def boss(self): ' The :class:`calibre.gui2.tweak_book.boss.Boss` object. Used to control the user interface. ' return get_boss()

@property def gui(self): ' The main window of the user interface ' return self.boss.gui

@property def current_container(self): ' Return the current :class:`calibre.ebooks.oeb.polish.container.Container` object that represents the book being edited. ' return current_container()

[belgeler] def register_shortcut(self, qaction, unique_name, default_keys=(), short_text=None, description=None, **extra_data): ''' Register a keyboard shortcut that will trigger the specified ``qaction``. This keyboard shortcut will become automatically customizable by the user in the Keyboard shortcuts section of the editor preferences.

:param qaction: A QAction object, it will be triggered when the configured key combination is pressed by the user. :param unique_name: A unique name for this shortcut/action. It will be used internally, it must not be shared by any other actions in this plugin. :param default_keys: A list of the default keyboard shortcuts. If not specified no default shortcuts will be set. If the shortcuts specified here conflict with either builtin shortcuts or shortcuts from user configuration/other plugins, they will be ignored. In that case, users will have to configure the shortcuts manually via Preferences. For example: ``default_keys=('Ctrl+J', 'F9')``. :param short_text: An optional short description of this action. If not specified the text from the QAction will be used. :param description: An optional longer description of this action, it will be used in the preferences entry for this shortcut. ''' short_text = short_text or str(qaction.text()).replace('&&', '\0').replace('&', '').replace('\0', '&') self.gui.keyboard.register_shortcut( self.name + '_' + unique_name, short_text, default_keys=default_keys, action=qaction, description=description or '', group=_('Plugins'))

[belgeler] def create_action(self, for_toolbar=True): ''' Create a QAction that will be added to either the plugins toolbar or the plugins menu depending on ``for_toolbar``. For example::

def create_action(self, for_toolbar=True): ac = QAction(get_icons('myicon.png'), 'Do something') if for_toolbar: # We want the toolbar button to have a popup menu menu = QMenu() ac.setMenu(menu) menu.addAction('Do something else') subaction = menu.addAction('And another')

# Register a keyboard shortcut for this toolbar action be # careful to do this for only one of the toolbar action or # the menu action, not both. self.register_shortcut(ac, 'some-unique-name', default_keys=('Ctrl+K',)) return ac

.. seealso:: Method :meth:`register_shortcut`. ''' raise NotImplementedError()

def load_plugin_tools(plugin): try: main = importlib.import_module(plugin.__class__.__module__+'.main') except ImportError: import traceback traceback.print_exc() else: for x in itervalues(vars(main)): if isinstance(x, type) and x is not Tool and issubclass(x, Tool): ans = x() ans.plugin = plugin yield ans def plugin_action_sid(plugin, tool, for_toolbar=True): return plugin.name + tool.name + ('toolbar' if for_toolbar else 'menu')

plugin_toolbar_actions = []

def create_plugin_action(plugin, tool, for_toolbar, actions=None, toolbar_actions=None, plugin_menu_actions=None): try: ac = tool.create_action(for_toolbar=for_toolbar) if ac is None: raise RuntimeError('create_action() failed to return an action') except Exception: prints('Failed to create action for tool:', tool.name) import traceback traceback.print_exc() return sid = plugin_action_sid(plugin, tool, for_toolbar) if actions is not None and sid in actions: prints(f'The {tool.name} tool from the {plugin.name} plugin has a non unique name, ignoring') else: if actions is not None: actions[sid] = ac ac.sid = sid if for_toolbar: if toolbar_actions is not None: toolbar_actions[sid] = ac plugin_toolbar_actions.append(ac) ac.popup_mode = {'instant':QToolButton.ToolButtonPopupMode.InstantPopup, 'button':QToolButton.ToolButtonPopupMode.MenuButtonPopup}.get( tool.toolbar_button_popup_mode, QToolButton.ToolButtonPopupMode.DelayedPopup) else: if plugin_menu_actions is not None: plugin_menu_actions.append(ac) return ac

_tool_memory = [] # Needed to prevent the tool object from being garbage collected def create_plugin_actions(actions, toolbar_actions, plugin_menu_actions): del _tool_memory[:] del plugin_toolbar_actions[:]

for plugin in all_edit_book_tool_plugins(): try: tools = tuple(load_plugin_tools(plugin)) except Exception: if plugin.installation_type is PluginInstallationType.BUILTIN: raise print('Failed to load third-party plugin:', plugin.name, file=sys.stderr) import traceback traceback.print_exc() continue for tool in tools: _tool_memory.append(tool) if tool.allowed_in_toolbar: create_plugin_action(plugin, tool, True, actions, toolbar_actions, plugin_menu_actions) if tool.allowed_in_menu: create_plugin_action(plugin, tool, False, actions, toolbar_actions, plugin_menu_actions)

def install_plugin(plugin): for tool in load_plugin_tools(plugin): if tool.allowed_in_toolbar: sid = plugin_action_sid(plugin, tool, True) if sid not in tprefs['global_plugins_toolbar']: tprefs['global_plugins_toolbar'] = tprefs['global_plugins_toolbar'] + [sid]

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.utils.formatter_functions

calibre.utils.formatter_functions öğesinin kaynak kodu

#!/usr/bin/env python

''' Created on 13 Jan 2011

@author: charles '''

__license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en'

import inspect import numbers import posixpath import re import traceback from contextlib import suppress from datetime import datetime, timedelta from enum import Enum, auto from functools import partial from math import ceil, floor, modf, trunc from lxml import html from calibre import human_readable, prepare_string_for_xml, prints from calibre.constants import DEBUG from calibre.db.constants import DATA_DIR_NAME, DATA_FILE_PATTERN from calibre.db.notes.exim import expand_note_resources, parse_html from calibre.ebooks.metadata import title_sort from calibre.utils.config import tweaks from calibre.utils.date import UNDEFINED_DATE, format_date, now, parse_date from calibre.utils.icu import capitalize, sort_key, strcmp from calibre.utils.icu import lower as icu_lower from calibre.utils.localization import _, calibre_langcode_to_name, canonicalize_lang from calibre.utils.titlecase import titlecase from polyglot.builtins import iteritems, itervalues class StoredObjectType(Enum): PythonFunction = auto() StoredGPMTemplate = auto() StoredPythonTemplate = auto()

class FormatterFunctions:

error_function_body = ('def evaluate(self, formatter, kwargs, mi, locals):\n' '\treturn "' + _('Duplicate user function name {0}. ' 'Change the name or ensure that the functions are identical') + '"')

def __init__(self): self._builtins = {} self._functions = {} self._functions_from_library = {}

def register_builtin(self, func_class): if not isinstance(func_class, FormatterFunction): raise ValueError('Class %s is not an instance of FormatterFunction'%( func_class.__class__.__name__)) name = func_class.name if name in self._functions: raise ValueError('Name %s already used'%name) self._builtins[name] = func_class self._functions[name] = func_class for a in func_class.aliases: self._functions[a] = func_class def _register_function(self, func_class, replace=False): if not isinstance(func_class, FormatterFunction): raise ValueError('Class %s is not an instance of FormatterFunction'%( func_class.__class__.__name__)) name = func_class.name if not replace and name in self._functions: raise ValueError('Name %s already used'%name) self._functions[name] = func_class def register_functions(self, library_uuid, funcs): self._functions_from_library[library_uuid] = funcs self._register_functions()

def _register_functions(self): for compiled_funcs in itervalues(self._functions_from_library): for cls in compiled_funcs: f = self._functions.get(cls.name, None) replace = False if f is not None: existing_body = f.program_text new_body = cls.program_text if new_body != existing_body: # Change the body of the template function to one that will # return an error message. Also change the arg count to # -1 (variable) to avoid template compilation errors if DEBUG: print(f'attempt to replace formatter function {f.name} with a different body') replace = True func = [cls.name, '', -1, self.error_function_body.format(cls.name)] cls = compile_user_function(*func) else: continue formatter_functions()._register_function(cls, replace=replace)

def unregister_functions(self, library_uuid): if library_uuid in self._functions_from_library: for cls in self._functions_from_library[library_uuid]: self._functions.pop(cls.name, None) self._functions_from_library.pop(library_uuid) self._register_functions()

def get_builtins(self): return self._builtins def get_builtins_and_aliases(self): res = {} for f in itervalues(self._builtins): res[f.name] = f for a in f.aliases: res[a] = f return res def get_functions(self): return self._functions def reset_to_builtins(self): self._functions = {} for n,c in self._builtins.items(): self._functions[n] = c for a in c.aliases: self._functions[a] = c

_ff = FormatterFunctions()

def formatter_functions(): global _ff return _ff def only_in_gui_error(name): raise ValueError(_('The function {} can be used only in the GUI').format(name))

def get_database(mi, name): proxy = mi.get('_proxy_metadata', None) if proxy is None: if name is not None: only_in_gui_error(name) return None wr = proxy.get('_db', None) if wr is None: if name is not None: raise ValueError(_('In function {}: The database has been closed').format(name)) return None cache = wr() if cache is None: if name is not None: raise ValueError(_('In function {}: The database has been closed').format(name)) return None wr = getattr(cache, 'library_database_instance', None) if wr is None: if name is not None: only_in_gui_error() return None db = wr() if db is None: if name is not None: raise ValueError(_('In function {}: The database has been closed').format(name)) return None return db class FormatterFunction:

doc = _('No documentation provided') name = 'no name provided' category = 'Unknown' arg_count = 0 aliases = [] object_type = StoredObjectType.PythonFunction def evaluate(self, formatter, kwargs, mi, locals, *args): raise NotImplementedError()

def eval_(self, formatter, kwargs, mi, locals, *args): ret = self.evaluate(formatter, kwargs, mi, locals, *args) if isinstance(ret, (bytes, str)): return ret if isinstance(ret, list): return ','.join(ret) if isinstance(ret, (numbers.Number, bool)): return str(ret)

def only_in_gui_error(self): only_in_gui_error(self.name)

def get_database(self, mi): return get_database(mi, self.name)

class BuiltinFormatterFunction(FormatterFunction):

def __init__(self): formatter_functions().register_builtin(self) eval_func = inspect.getmembers(self.__class__, lambda x: inspect.isfunction(x) and x.__name__ == 'evaluate') try: lines = [l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]] except: lines = [] self.program_text = ''.join(lines)

[belgeler] class BuiltinStrcmp(BuiltinFormatterFunction): name = 'strcmp' arg_count = 5 category = 'Relational' __doc__ = doc = _('strcmp(x, y, lt, eq, gt) -- does a case-insensitive comparison of x ' 'and y as strings. Returns lt if x < y. Returns eq if x == y. ' 'Otherwise returns gt. In many cases the lexical comparison operators ' '(>, <, == etc) can replace this function.')

def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): v = strcmp(x, y) if v < 0: return lt if v == 0: return eq return gt

[belgeler] class BuiltinStrcmpcase(BuiltinFormatterFunction): name = 'strcmpcase' arg_count = 5 category = 'Relational' __doc__ = doc = _('strcmpcase(x, y, lt, eq, gt) -- does a case-sensitive comparison of x ' 'and y as strings. Returns lt if x < y. Returns eq if x == y. ' 'Otherwise returns gt.\n' 'Note: This is NOT the default behavior used by calibre, for example, in the ' 'lexical comparison operators (==, >, <, etc.). This function could ' 'cause unexpected results, preferably use strcmp() whenever possible.')

def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): from calibre.utils.icu import case_sensitive_strcmp as case_strcmp v = case_strcmp(x, y) if v < 0: return lt if v == 0: return eq return gt

[belgeler] class BuiltinCmp(BuiltinFormatterFunction): name = 'cmp' category = 'Relational' arg_count = 5 __doc__ = doc = _('cmp(x, y, lt, eq, gt) -- compares x and y after converting both to ' 'numbers. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt. ' 'In many cases the numeric comparison operators ' '(>#, <#, ==# etc) can replace this function.')

def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): x = float(x if x and x != 'None' else 0) y = float(y if y and y != 'None' else 0) if x < y: return lt if x == y: return eq return gt

[belgeler] class BuiltinFirstMatchingCmp(BuiltinFormatterFunction): name = 'first_matching_cmp' category = 'Relational' arg_count = -1 __doc__ = doc = _('first_matching_cmp(val, [cmp1, result1,]+, else_result) -- ' 'compares "val < cmpN" in sequence, returning resultN for ' 'the first comparison that succeeds. Returns else_result ' 'if no comparison succeeds. Example: ' 'first_matching_cmp(10,5,"small",10,"middle",15,"large","giant") ' 'returns "large". The same example with a first value of 16 returns "giant".')

def evaluate(self, formatter, kwargs, mi, locals, *args): if (len(args) % 2) != 0: raise ValueError(_('first_matching_cmp requires an even number of arguments')) val = float(args[0] if args[0] and args[0] != 'None' else 0) for i in range(1, len(args) - 1, 2): c = float(args[i] if args[i] and args[i] != 'None' else 0) if val < c: return args[i+1] return args[len(args)-1]

[belgeler] class BuiltinStrcat(BuiltinFormatterFunction): name = 'strcat' arg_count = -1 category = 'String manipulation' __doc__ = doc = _('strcat(a [, b]*) -- can take any number of arguments. Returns the ' 'string formed by concatenating all the arguments')

def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 res = '' for i in range(0, len(args)): res += args[i] return res

[belgeler] class BuiltinStrlen(BuiltinFormatterFunction): name = 'strlen' arg_count = 1 category = 'String manipulation' __doc__ = doc = _('strlen(a) -- Returns the length of the string passed as ' 'the argument')

def evaluate(self, formatter, kwargs, mi, locals, a): try: return len(a) except: return -1

[belgeler] class BuiltinAdd(BuiltinFormatterFunction): name = 'add' arg_count = -1 category = 'Arithmetic' __doc__ = doc = _('add(x [, y]*) -- returns the sum of its arguments. ' 'Throws an exception if an argument is not a number. ' 'This function can often be ' 'replaced with the + operator.')

def evaluate(self, formatter, kwargs, mi, locals, *args): res = 0 for v in args: v = float(v if v and v != 'None' else 0) res += v return str(res)

[belgeler] class BuiltinSubtract(BuiltinFormatterFunction): name = 'subtract' arg_count = 2 category = 'Arithmetic' __doc__ = doc = _('subtract(x, y) -- returns x - y. Throws an exception if ' 'either x or y are not numbers. This function can often be ' 'replaced with the - operator.')

def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x and x != 'None' else 0) y = float(y if y and y != 'None' else 0) return str(x - y)

[belgeler] class BuiltinMultiply(BuiltinFormatterFunction): name = 'multiply' arg_count = -1 category = 'Arithmetic' __doc__ = doc = _('multiply(x [, y]*) -- returns the product of its arguments. ' 'Throws an exception if any argument is not a number. ' 'This function can often be replaced with the * operator.')

def evaluate(self, formatter, kwargs, mi, locals, *args): res = 1 for v in args: v = float(v if v and v != 'None' else 0) res *= v return str(res)

[belgeler] class BuiltinDivide(BuiltinFormatterFunction): name = 'divide' arg_count = 2 category = 'Arithmetic' __doc__ = doc = _('divide(x, y) -- returns x / y. Throws an exception if ' 'either x or y are not numbers.' ' This function can often be replaced with the / operator.')

def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x and x != 'None' else 0) y = float(y if y and y != 'None' else 0) return str(x / y)

[belgeler] class BuiltinCeiling(BuiltinFormatterFunction): name = 'ceiling' arg_count = 1 category = 'Arithmetic' __doc__ = doc = _('ceiling(x) -- returns the smallest integer greater ' 'than or equal to x. Throws an exception if x is ' 'not a number.')

def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) return str(int(ceil(x)))

[belgeler] class BuiltinFloor(BuiltinFormatterFunction): name = 'floor' arg_count = 1 category = 'Arithmetic' __doc__ = doc = _('floor(x) -- returns the largest integer less ' 'than or equal to x. Throws an exception if x is ' 'not a number.')

def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) return str(int(floor(x)))

[belgeler] class BuiltinRound(BuiltinFormatterFunction): name = 'round' arg_count = 1 category = 'Arithmetic' __doc__ = doc = _('round(x) -- returns the nearest integer to x. ' 'Throws an exception if x is not a number.')

def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) return str(int(round(x)))

[belgeler] class BuiltinMod(BuiltinFormatterFunction): name = 'mod' arg_count = 2 category = 'Arithmetic' __doc__ = doc = _('mod(x) -- returns floor(remainder of x / y). ' 'Throws an exception if either x or y is not a number.')

def evaluate(self, formatter, kwargs, mi, locals, x, y): x = float(x if x and x != 'None' else 0) y = float(y if y and y != 'None' else 0) return str(int(x % y))

[belgeler] class BuiltinFractionalPart(BuiltinFormatterFunction): name = 'fractional_part' arg_count = 1 category = 'Arithmetic' __doc__ = doc = _('fractional_part(x) -- returns the value after the decimal ' 'point. For example, fractional_part(3.14) returns 0.14. ' 'Throws an exception if x is not a number.')

def evaluate(self, formatter, kwargs, mi, locals, x): x = float(x if x and x != 'None' else 0) return str(modf(x)[0])

[belgeler] class BuiltinTemplate(BuiltinFormatterFunction): name = 'template' arg_count = 1 category = 'Recursion'

__doc__ = doc = _('template(x) -- evaluates x as a template. The evaluation is done ' 'in its own context, meaning that variables are not shared between ' 'the caller and the template evaluation. Because the { and } ' 'characters are special, you must use [[ for the { character and ' ']] for the } character; they are converted automatically. ' 'For example, template(\'[[title_sort]]\') will evaluate the ' 'template {title_sort} and return its value. Note also that ' 'prefixes and suffixes (the `|prefix|suffix` syntax) cannot be ' 'used in the argument to this function when using template program mode.')

def evaluate(self, formatter, kwargs, mi, locals, template): template = template.replace('[[', '{').replace(']]', '}') return formatter.__class__().safe_format(template, kwargs, 'TEMPLATE', mi)

[belgeler] class BuiltinEval(BuiltinFormatterFunction): name = 'eval' arg_count = 1 category = 'Recursion' __doc__ = doc = _('eval(template) -- evaluates the template, passing the local ' 'variables (those \'assign\'ed to) instead of the book metadata. ' ' This permits using the template processor to construct complex ' 'results from local variables. Because the { and } ' 'characters are special, you must use [[ for the { character and ' ']] for the } character; they are converted automatically. ' 'Note also that prefixes and suffixes (the `|prefix|suffix` syntax) ' 'cannot be used in the argument to this function when using ' 'template program mode.')

def evaluate(self, formatter, kwargs, mi, locals, template): from calibre.utils.formatter import EvalFormatter template = template.replace('[[', '{').replace(']]', '}') return EvalFormatter().safe_format(template, locals, 'EVAL', None)

[belgeler] class BuiltinAssign(BuiltinFormatterFunction): name = 'assign' arg_count = 2 category = 'Other' __doc__ = doc = _('assign(id, val) -- assigns val to id, then returns val. ' 'id must be an identifier, not an expression. ' 'This function can often be replaced with the = operator.')

def evaluate(self, formatter, kwargs, mi, locals, target, value): locals[target] = value return value

[belgeler] class BuiltinListSplit(BuiltinFormatterFunction): name = 'list_split' arg_count = 3 category = 'List manipulation' __doc__ = doc = _('list_split(list_val, sep, id_prefix) -- splits the list_val ' "into separate values using 'sep', then assigns the values " "to variables named 'id_prefix_N' where N is the position " "of the value in the list. The first item has position 0 (zero). " "The function returns the last element in the list. " "Example: split('one:two:foo', ':', 'var') is equivalent " "to var_0 = 'one'; var_1 = 'two'; var_2 = 'foo'.")

def evaluate(self, formatter, kwargs, mi, locals, list_val, sep, id_prefix): l = [v.strip() for v in list_val.split(sep)] res = '' for i,v in enumerate(l): res = locals[id_prefix+'_'+str(i)] = v return res

[belgeler] class BuiltinPrint(BuiltinFormatterFunction): name = 'print' arg_count = -1 category = 'Other' __doc__ = doc = _('print(a[, b]*) -- prints the arguments to standard output. ' 'Unless you start calibre from the command line (calibre-debug -g), ' 'the output will go to a black hole.')

def evaluate(self, formatter, kwargs, mi, locals, *args): print(args) return ''

[belgeler] class BuiltinField(BuiltinFormatterFunction): name = 'field' arg_count = 1 category = 'Get values from metadata' __doc__ = doc = _('field(lookup_name) -- returns the metadata field named by lookup_name')

def evaluate(self, formatter, kwargs, mi, locals, name): return formatter.get_value(name, [], kwargs)

[belgeler] class BuiltinRawField(BuiltinFormatterFunction): name = 'raw_field' arg_count = -1 category = 'Get values from metadata' __doc__ = doc = _('raw_field(lookup_name [, optional_default]) -- returns the ' 'metadata field named by lookup_name without applying any formatting. ' 'It evaluates and returns the optional second argument ' "'default' if the field is undefined ('None').")

def evaluate(self, formatter, kwargs, mi, locals, name, default=None): res = getattr(mi, name, None) if res is None and default is not None: return default if isinstance(res, list): fm = mi.metadata_for_field(name) if fm is None: return ', '.join(res) return fm['is_multiple']['list_to_ui'].join(res) return str(res)

[belgeler] class BuiltinRawList(BuiltinFormatterFunction): name = 'raw_list' arg_count = 2 category = 'Get values from metadata' __doc__ = doc = _('raw_list(lookup_name, separator) -- returns the metadata list ' 'named by lookup_name without applying any formatting or sorting and ' 'with items separated by separator.')

def evaluate(self, formatter, kwargs, mi, locals, name, separator): res = getattr(mi, name, None) if not isinstance(res, list): return "%s is not a list" % name return separator.join(res)

[belgeler] class BuiltinSubstr(BuiltinFormatterFunction): name = 'substr' arg_count = 3 category = 'String manipulation' __doc__ = doc = _('substr(str, start, end) -- returns the start\'th through the end\'th ' 'characters of str. The first character in str is the zero\'th ' 'character. If end is negative, then it indicates that many ' 'characters counting from the right. If end is zero, then it ' 'indicates the last character. For example, substr(\'12345\', 1, 0) ' 'returns \'2345\', and substr(\'12345\', 1, -1) returns \'234\'.')

def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_): return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)]

[belgeler] class BuiltinLookup(BuiltinFormatterFunction): name = 'lookup' arg_count = -1 category = 'Iterating over values' __doc__ = doc = _('lookup(val, [pattern, field,]+ else_field) -- ' 'like switch, except the arguments are field (metadata) names, not ' 'text. The value of the appropriate field will be fetched and used. ' 'Note that because composite columns are fields, you can use this ' 'function in one composite field to use the value of some other ' 'composite field. This is extremely useful when constructing ' 'variable save paths')

def evaluate(self, formatter, kwargs, mi, locals, val, *args): if len(args) == 2: # here for backwards compatibility if val: return formatter.vformat('{'+args[0].strip()+'}', [], kwargs) else: return formatter.vformat('{'+args[1].strip()+'}', [], kwargs) if (len(args) % 2) != 1: raise ValueError(_('lookup requires either 2 or an odd number of arguments')) i = 0 while i < len(args): if i + 1 >= len(args): return formatter.vformat('{' + args[i].strip() + '}', [], kwargs) if re.search(args[i], val, flags=re.I): return formatter.vformat('{'+args[i+1].strip() + '}', [], kwargs) i += 2

[belgeler] class BuiltinTest(BuiltinFormatterFunction): name = 'test' arg_count = 3 category = 'If-then-else' __doc__ = doc = _('test(val, text if not empty, text if empty) -- return `text if not ' 'empty` if val is not empty, otherwise return `text if empty`')

def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set): if val: return value_if_set else: return value_not_set

[belgeler] class BuiltinContains(BuiltinFormatterFunction): name = 'contains' arg_count = 4 category = 'If-then-else' __doc__ = doc = _('contains(val, pattern, text if match, text if not match) -- checks ' 'if val contains matches for the regular expression `pattern`. ' 'Returns `text if match` if matches are found, otherwise it returns ' '`text if no match`')

def evaluate(self, formatter, kwargs, mi, locals, val, test, value_if_present, value_if_not): if re.search(test, val, flags=re.I): return value_if_present else: return value_if_not

[belgeler] class BuiltinSwitch(BuiltinFormatterFunction): name = 'switch' arg_count = -1 category = 'Iterating over values' __doc__ = doc = _('switch(val, [pattern, value,]+ else_value) -- ' 'for each `pattern, value` pair, checks if `val` matches ' 'the regular expression `pattern` and if so, returns that ' '`value`. If no pattern matches, then `else_value` is returned. ' 'You can have as many `pattern, value` pairs as you want')

def evaluate(self, formatter, kwargs, mi, locals, val, *args): if (len(args) % 2) != 1: raise ValueError(_('switch requires an even number of arguments')) i = 0 while i < len(args): if i + 1 >= len(args): return args[i] if re.search(args[i], val, flags=re.I): return args[i+1] i += 2

[belgeler] class BuiltinSwitchIf(BuiltinFormatterFunction): name = 'switch_if' arg_count = -1 category = 'Iterating over values' __doc__ = doc = _('switch_if([test_expression, value_expression,]+ else_expression) -- ' 'for each "test_expression, value_expression" pair, checks if test_expression ' 'is True (non-empty) and if so returns the result of value_expression. ' 'If no test_expression is True then the result of else_expression is returned. ' 'You can have as many "test_expression, value_expression" pairs as you want.')

def evaluate(self, formatter, kwargs, mi, locals, *args): if (len(args) % 2) != 1: raise ValueError(_('switch_if requires an odd number of arguments')) # We shouldn't get here because the function is inlined. However, someone # might call it directly. i = 0 while i < len(args): if i + 1 >= len(args): return args[i] if args[i]: return args[i+1] i += 2

[belgeler] class BuiltinStrcatMax(BuiltinFormatterFunction): name = 'strcat_max' arg_count = -1 category = 'String manipulation' __doc__ = doc = _('strcat_max(max, string1 [, prefix2, string2]*) -- ' 'Returns a string formed by concatenating the arguments. The ' 'returned value is initialized to string1. `Prefix, string` ' 'pairs are added to the end of the value as long as the ' 'resulting string length is less than `max`. String1 is returned ' 'even if string1 is longer than max. You can pass as many ' '`prefix, string` pairs as you wish.')

def evaluate(self, formatter, kwargs, mi, locals, *args): if len(args) < 2: raise ValueError(_('strcat_max requires 2 or more arguments')) if (len(args) % 2) != 0: raise ValueError(_('strcat_max requires an even number of arguments')) try: max = int(args[0]) except: raise ValueError(_('first argument to strcat_max must be an integer'))

i = 2 result = args[1] try: while i < len(args): if (len(result) + len(args[i]) + len(args[i+1])) > max: break result = result + args[i] + args[i+1] i += 2 except: pass return result.strip()

[belgeler] class BuiltinInList(BuiltinFormatterFunction): name = 'in_list' arg_count = -1 category = 'List lookup' __doc__ = doc = _('in_list(val, separator, [ pattern, found_val, ]+ not_found_val) -- ' 'treating val as a list of items separated by separator, ' 'if the pattern matches any of the list values then return found_val.' 'If the pattern matches no list value then return ' 'not_found_val. The pattern and found_value pairs can be repeated as ' 'many times as desired. The patterns are checked in order. The ' 'found_val for the first match is returned. ' 'Aliases: in_list(), list_contains()') aliases = ['list_contains']

def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args): if (len(args) % 2) != 1: raise ValueError(_('in_list requires an odd number of arguments')) l = [v.strip() for v in val.split(sep) if v.strip()] i = 0 while i < len(args): if i + 1 >= len(args): return args[i] sf = args[i] fv = args[i+1] if l: for v in l: if re.search(sf, v, flags=re.I): return fv i += 2

[belgeler] class BuiltinStrInList(BuiltinFormatterFunction): name = 'str_in_list' arg_count = -1 category = 'List lookup' __doc__ = doc = _('str_in_list(val, separator, [string, found_val, ]+ not_found_val) -- ' 'treating val as a list of items separated by separator, if the ' 'string matches any of the list values then return found_val.' 'If the string matches no list value then return ' 'not_found_val. The comparison is exact match (not contains) and is ' 'case insensitive. The string and found_value pairs can be repeated as ' 'many times as desired. The patterns are checked in order. The ' 'found_val for the first match is returned.')

def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args): if (len(args) % 2) != 1: raise ValueError(_('str_in_list requires an odd number of arguments')) l = [v.strip() for v in val.split(sep) if v.strip()] i = 0 while i < len(args): if i + 1 >= len(args): return args[i] sf = args[i] fv = args[i+1] c = [v.strip() for v in sf.split(sep) if v.strip()] if l: for v in l: for t in c: if strcmp(t, v) == 0: return fv i += 2

[belgeler] class BuiltinIdentifierInList(BuiltinFormatterFunction): name = 'identifier_in_list' arg_count = -1 category = 'List lookup' __doc__ = doc = _('identifier_in_list(val, id_name [, found_val, not_found_val]) -- ' 'treat val as a list of identifiers separated by commas. An identifier ' 'has the format "id_name:value". The id_name parameter is the id_name ' 'text to search for, either "id_name" or "id_name:regexp". The first case ' 'matches if there is any identifier matching that id_name. The second ' 'case matches if id_name matches an identifier and the regexp ' 'matches the identifier\'s value. If found_val and not_found_val ' 'are provided then if there is a match then return found_val, otherwise ' 'return not_found_val. If found_val and not_found_val are not ' 'provided then if there is a match then return the identifier:value ' 'pair, otherwise the empty string.')

def evaluate(self, formatter, kwargs, mi, locals, val, ident, *args): if len(args) == 0: fv_is_id = True nfv = '' elif len(args) == 2: fv_is_id = False fv = args[0] nfv = args[1] else: raise ValueError(_("{} requires 2 or 4 arguments").format(self.name))

l = [v.strip() for v in val.split(',') if v.strip()] (id_, __, regexp) = ident.partition(':') if not id_: return nfv for candidate in l: i, __, v = candidate.partition(':') if v and i == id_: if not regexp or re.search(regexp, v, flags=re.I): return candidate if fv_is_id else fv return nfv

[belgeler] class BuiltinRe(BuiltinFormatterFunction): name = 're' arg_count = 3 category = 'String manipulation' __doc__ = doc = _('re(val, pattern, replacement) -- return val after applying ' 'the regular expression. All instances of `pattern` are replaced ' 'with `replacement`. As in all of calibre, these are ' 'Python-compatible regular expressions')

def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement): return re.sub(pattern, replacement, val, flags=re.I)

[belgeler] class BuiltinReGroup(BuiltinFormatterFunction): name = 're_group' arg_count = -1 category = 'String manipulation' __doc__ = doc = _('re_group(val, pattern [, template_for_group]*) -- ' 'return a string made by applying the regular expression pattern ' 'to the val and replacing each matched instance with the string ' 'computed by replacing each matched group by the value returned ' 'by the corresponding template. The original matched value for the ' 'group is available as $. In template program mode, like for ' 'the template and the eval functions, you use [[ for { and ]] for }.' ' The following example in template program mode looks for series ' 'with more than one word and uppercases the first word: ' "{series:'re_group($, \"(\\S* )(.*)\", \"[[$:uppercase()]]\", \"[[$]]\")'}")

def evaluate(self, formatter, kwargs, mi, locals, val, pattern, *args): from calibre.utils.formatter import EvalFormatter def repl(mo): res = '' if mo and mo.lastindex: for dex in range(0, mo.lastindex): gv = mo.group(dex+1) if gv is None: continue if len(args) > dex: template = args[dex].replace('[[', '{').replace(']]', '}') res += EvalFormatter().safe_format(template, {'$': gv}, 'EVAL', None, strip_results=False) else: res += gv return res return re.sub(pattern, repl, val, flags=re.I)

[belgeler] class BuiltinSwapAroundComma(BuiltinFormatterFunction): name = 'swap_around_comma' arg_count = 1 category = 'String manipulation' __doc__ = doc = _('swap_around_comma(val) -- given a value of the form ' '"B, A", return "A B". This is most useful for converting names ' 'in LN, FN format to FN LN. If there is no comma, the function ' 'returns val unchanged')

def evaluate(self, formatter, kwargs, mi, locals, val): return re.sub(r'^(.*?),\s*(.*$)', r'\2 \1', val, flags=re.I).strip()

[belgeler] class BuiltinIfempty(BuiltinFormatterFunction): name = 'ifempty' arg_count = 2 category = 'If-then-else' __doc__ = doc = _('ifempty(val, text if empty) -- return val if val is not empty, ' 'otherwise return `text if empty`')

def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty): if val: return val else: return value_if_empty

[belgeler] class BuiltinShorten(BuiltinFormatterFunction): name = 'shorten' arg_count = 4 category = 'String manipulation' __doc__ = doc = _('shorten(val, left chars, middle text, right chars) -- Return a ' 'shortened version of val, consisting of `left chars` ' 'characters from the beginning of val, followed by ' '`middle text`, followed by `right chars` characters from ' 'the end of the string. `Left chars` and `right chars` must be ' 'integers. For example, assume the title of the book is ' '`Ancient English Laws in the Times of Ivanhoe`, and you want ' 'it to fit in a space of at most 15 characters. If you use ' '{title:shorten(9,-,5)}, the result will be `Ancient E-anhoe`. ' 'If the field\'s length is less than left chars + right chars + ' 'the length of `middle text`, then the field will be used ' 'intact. For example, the title `The Dome` would not be changed.')

def evaluate(self, formatter, kwargs, mi, locals, val, leading, center_string, trailing): l = max(0, int(leading)) t = max(0, int(trailing)) if len(val) > l + len(center_string) + t: return val[0:l] + center_string + ('' if t == 0 else val[-t:]) else: return val

[belgeler] class BuiltinCount(BuiltinFormatterFunction): name = 'count' arg_count = 2 category = 'List manipulation' aliases = ['list_count']

__doc__ = doc = _('count(val, separator) -- interprets the value as a list of items ' 'separated by `separator`, returning the number of items in the ' 'list. Most lists use a comma as the separator, but authors ' 'uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}. ' 'Aliases: count(), list_count()')

def evaluate(self, formatter, kwargs, mi, locals, val, sep): return str(len([v for v in val.split(sep) if v]))

[belgeler] class BuiltinListCountMatching(BuiltinFormatterFunction): name = 'list_count_matching' arg_count = 3 category = 'List manipulation' aliases = ['count_matching']

__doc__ = doc = _('list_count_matching(list, pattern, separator) -- ' "interprets 'list' as a list of items separated by 'separator', " 'returning the number of items in the list that match the regular ' "expression 'pattern'. Aliases: list_count_matching(), count_matching()")

def evaluate(self, formatter, kwargs, mi, locals, list_, pattern, sep): res = 0 for v in [x.strip() for x in list_.split(sep) if x.strip()]: if re.search(pattern, v, flags=re.I): res += 1 return str(res)

[belgeler] class BuiltinListitem(BuiltinFormatterFunction): name = 'list_item' arg_count = 3 category = 'List lookup' __doc__ = doc = _('list_item(val, index, separator) -- interpret the value as a list of ' 'items separated by `separator`, returning the `index`th item. ' 'The first item is number zero. The last item can be returned ' 'using `list_item(-1,separator)`. If the item is not in the list, ' 'then the empty value is returned. The separator has the same ' 'meaning as in the count function.')

def evaluate(self, formatter, kwargs, mi, locals, val, index, sep): if not val: return '' index = int(index) val = val.split(sep) try: return val[index].strip() except: return ''

[belgeler] class BuiltinSelect(BuiltinFormatterFunction): name = 'select' arg_count = 2 category = 'List lookup' __doc__ = doc = _('select(val, key) -- interpret the value as a comma-separated list ' 'of items, with the items being "id:value". Find the pair with the ' 'id equal to key, and return the corresponding value. Returns the ' 'empty string if no match is found.' )

def evaluate(self, formatter, kwargs, mi, locals, val, key): if not val: return '' vals = [v.strip() for v in val.split(',')] tkey = key+':' for v in vals: if v.startswith(tkey): return v[len(tkey):] return ''

[belgeler] class BuiltinApproximateFormats(BuiltinFormatterFunction): name = 'approximate_formats' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('approximate_formats() -- return a comma-separated ' 'list of formats that at one point were associated with the ' 'book. There is no guarantee that this list is correct, ' 'although it probably is. ' 'This function can be called in template program mode using ' 'the template "{:\'approximate_formats()\'}". ' 'Note that format names are always uppercase, as in EPUB. ' 'This function works only in the GUI. If you want to use these values ' 'in save-to-disk or send-to-device templates then you ' 'must make a custom "Column built from other columns", use ' 'the function in that column\'s template, and use that ' 'column\'s value in your save/send templates' )

def evaluate(self, formatter, kwargs, mi, locals): if hasattr(mi, '_proxy_metadata'): fmt_data = mi._proxy_metadata.db_approx_formats if not fmt_data: return '' data = sorted(fmt_data) return ','.join(v.upper() for v in data) self.only_in_gui_error()

[belgeler] class BuiltinFormatsModtimes(BuiltinFormatterFunction): name = 'formats_modtimes' arg_count = 1 category = 'Get values from metadata' __doc__ = doc = _('formats_modtimes(date_format) -- return a comma-separated ' 'list of colon-separated items representing modification times ' 'for the formats of a book. The date_format parameter ' 'specifies how the date is to be formatted. See the ' 'format_date function for details. You can use the select ' 'function to get the mod time for a specific ' 'format. Note that format names are always uppercase, ' 'as in EPUB.' )

def evaluate(self, formatter, kwargs, mi, locals, fmt): fmt_data = mi.get('format_metadata', {}) try: data = sorted(fmt_data.items(), key=lambda x:x[1]['mtime'], reverse=True) return ','.join(k.upper()+':'+format_date(v['mtime'], fmt) for k,v in data) except: return ''

[belgeler] class BuiltinFormatsSizes(BuiltinFormatterFunction): name = 'formats_sizes' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('formats_sizes() -- return a comma-separated list of ' 'colon-separated items representing sizes in bytes ' 'of the formats of a book. You can use the select ' 'function to get the size for a specific ' 'format. Note that format names are always uppercase, ' 'as in EPUB.' )

def evaluate(self, formatter, kwargs, mi, locals): fmt_data = mi.get('format_metadata', {}) try: return ','.join(k.upper()+':'+str(v['size']) for k,v in iteritems(fmt_data)) except: return ''

[belgeler] class BuiltinFormatsPaths(BuiltinFormatterFunction): name = 'formats_paths' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('formats_paths() -- return a comma-separated list of ' 'colon-separated items representing full path to ' 'the formats of a book. You can use the select ' 'function to get the path for a specific ' 'format. Note that format names are always uppercase, ' 'as in EPUB.')

def evaluate(self, formatter, kwargs, mi, locals): fmt_data = mi.get('format_metadata', {}) try: return ','.join(k.upper()+':'+str(v['path']) for k,v in iteritems(fmt_data)) except: return ''

[belgeler] class BuiltinHumanReadable(BuiltinFormatterFunction): name = 'human_readable' arg_count = 1 category = 'Formatting values' __doc__ = doc = _('human_readable(v) -- return a string ' 'representing the number v in KB, MB, GB, etc.' )

def evaluate(self, formatter, kwargs, mi, locals, val): try: return human_readable(round(float(val))) except: return ''

[belgeler] class BuiltinFormatNumber(BuiltinFormatterFunction): name = 'format_number' arg_count = 2 category = 'Formatting values' __doc__ = doc = _('format_number(v, template) -- format the number v using ' 'a Python formatting template such as "{0:5.2f}" or ' '"{0:,d}" or "${0:5,.2f}". The field_name part of the ' 'template must be a 0 (zero) (the "{0:" in the above examples). ' 'See the template language and Python documentation for more ' 'examples. You can leave off the leading "{0:" and trailing ' '"}" if the template contains only a format. Returns the empty ' 'string if formatting fails.' )

def evaluate(self, formatter, kwargs, mi, locals, val, template): if val == '' or val == 'None': return '' if '{' not in template: template = '{0:' + template + '}' try: v1 = float(val) except: return '' try: # Try formatting the value as a float return template.format(v1) except: pass try: # Try formatting the value as an int v2 = trunc(v1) if v2 == v1: return template.format(v2) except: pass return ''

[belgeler] class BuiltinSublist(BuiltinFormatterFunction): name = 'sublist' arg_count = 4 category = 'List manipulation' __doc__ = doc = _('sublist(val, start_index, end_index, separator) -- interpret the ' 'value as a list of items separated by `separator`, returning a ' 'new list made from the `start_index` to the `end_index` item. ' 'The first item is number zero. If an index is negative, then it ' 'counts from the end of the list. As a special case, an end_index ' 'of zero is assumed to be the length of the list. Examples using ' 'basic template mode and assuming that the tags column (which is ' 'comma-separated) contains "A, B, C": ' '{tags:sublist(0,1,\\\\,)} returns "A". ' '{tags:sublist(-1,0,\\\\,)} returns "C". ' '{tags:sublist(0,-1,\\\\,)} returns "A, B".' )

def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep): if not val: return '' si = int(start_index) ei = int(end_index) # allow empty list items so counts are what the user expects val = [v.strip() for v in val.split(sep)]

if sep == ',': sep = ', ' try: if ei == 0: return sep.join(val[si:]) else: return sep.join(val[si:ei]) except: return ''

[belgeler] class BuiltinSubitems(BuiltinFormatterFunction): name = 'subitems' arg_count = 3 category = 'List manipulation' __doc__ = doc = _('subitems(val, start_index, end_index) -- This function is used to ' 'break apart lists of items such as genres. It interprets the value ' 'as a comma-separated list of items, where each item is a period-' 'separated list. Returns a new list made by first finding all the ' 'period-separated items, then for each such item extracting the ' '`start_index` to the `end_index` components, then combining ' 'the results back together. The first component in a period-' 'separated list has an index of zero. If an index is negative, ' 'then it counts from the end of the list. As a special case, an ' 'end_index of zero is assumed to be the length of the list. ' 'Example using basic template mode and assuming a #genre value of ' '"A.B.C": {#genre:subitems(0,1)} returns "A". {#genre:subitems(0,2)} ' 'returns "A.B". {#genre:subitems(1,0)} returns "B.C". Assuming a #genre ' 'value of "A.B.C, D.E.F", {#genre:subitems(0,1)} returns "A, D". ' '{#genre:subitems(0,2)} returns "A.B, D.E"')

period_pattern = re.compile(r'(?<=[^\.\s])\.(?=[^\.\s])', re.U)

def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index): if not val: return '' si = int(start_index) ei = int(end_index) has_periods = '.' in val items = [v.strip() for v in val.split(',') if v.strip()] rv = set() for item in items: if has_periods and '.' in item: components = self.period_pattern.split(item) else: components = [item] try: if ei == 0: t = '.'.join(components[si:]).strip() else: t = '.'.join(components[si:ei]).strip() if t: rv.add(t) except: pass return ', '.join(sorted(rv, key=sort_key))

[belgeler] class BuiltinFormatDate(BuiltinFormatterFunction): name = 'format_date' arg_count = 2 category = 'Formatting values' __doc__ = doc = _('format_date(val, format_string) -- format the value, ' 'which must be a date, using the format_string, returning a string. ' 'The formatting codes are: ' 'd : the day as number without a leading zero (1 to 31) ' 'dd : the day as number with a leading zero (01 to 31) ' 'ddd : the abbreviated localized day name (e.g. "Mon" to "Sun"). ' 'dddd : the long localized day name (e.g. "Monday" to "Sunday"). ' 'M : the month as number without a leading zero (1 to 12). ' 'MM : the month as number with a leading zero (01 to 12) ' 'MMM : the abbreviated localized month name (e.g. "Jan" to "Dec"). ' 'MMMM : the long localized month name (e.g. "January" to "December"). ' 'yy : the year as two digit number (00 to 99). ' 'yyyy : the year as four digit number. ' 'h : the hours without a leading 0 (0 to 11 or 0 to 23, depending on am/pm) ' 'hh : the hours with a leading 0 (00 to 11 or 00 to 23, depending on am/pm) ' 'm : the minutes without a leading 0 (0 to 59) ' 'mm : the minutes with a leading 0 (00 to 59) ' 's : the seconds without a leading 0 (0 to 59) ' 'ss : the seconds with a leading 0 (00 to 59) ' 'ap : use a 12-hour clock instead of a 24-hour clock, with "ap" replaced by the localized string for am or pm ' 'AP : use a 12-hour clock instead of a 24-hour clock, with "AP" replaced by the localized string for AM or PM ' 'iso : the date with time and timezone. Must be the only format present ' 'to_number: the date as a floating point number ' 'from_number[:fmt]: format the timestamp using fmt if present otherwise iso')

def evaluate(self, formatter, kwargs, mi, locals, val, format_string): if not val or val == 'None': return '' try: if format_string == 'to_number': s = parse_date(val).timestamp() elif format_string.startswith('from_number'): val = datetime.fromtimestamp(float(val)) f = format_string[12:] s = format_date(val, f if f else 'iso') else: s = format_date(parse_date(val), format_string) return s except: s = 'BAD DATE' return s

[belgeler] class BuiltinFormatDateField(BuiltinFormatterFunction): name = 'format_date_field' arg_count = 2 category = 'Formatting values' __doc__ = doc = _("format_date_field(field_name, format_string) -- format " "the value in the field 'field_name', which must be the lookup name " "of date field, either standard or custom. See 'format_date' for " "the formatting codes. This function is much faster than format_date " "and should be used when you are formatting the value in a field " "(column). It can't be used for computed dates or dates in string " "variables. Example: format_date_field('pubdate', 'yyyy.MM.dd')")

def evaluate(self, formatter, kwargs, mi, locals, field, format_string): try: if field not in mi.all_field_keys(): return _('Unknown field %s passed to function %s')%(field, 'format_date_field') val = mi.get(field, None) if val is None: s = '' elif format_string == 'to_number': s = val.timestamp() elif format_string.startswith('from_number'): val = datetime.fromtimestamp(float(val)) f = format_string[12:] s = format_date(val, f if f else 'iso') else: s = format_date(val, format_string) return s except: traceback.print_exc() s = 'BAD DATE' return s

[belgeler] class BuiltinUppercase(BuiltinFormatterFunction): name = 'uppercase' arg_count = 1 category = 'String case changes' __doc__ = doc = _('uppercase(val) -- return val in upper case')

def evaluate(self, formatter, kwargs, mi, locals, val): return val.upper()

[belgeler] class BuiltinLowercase(BuiltinFormatterFunction): name = 'lowercase' arg_count = 1 category = 'String case changes' __doc__ = doc = _('lowercase(val) -- return val in lower case')

def evaluate(self, formatter, kwargs, mi, locals, val): return val.lower()

[belgeler] class BuiltinTitlecase(BuiltinFormatterFunction): name = 'titlecase' arg_count = 1 category = 'String case changes' __doc__ = doc = _('titlecase(val) -- return val in title case')

def evaluate(self, formatter, kwargs, mi, locals, val): return titlecase(val)

[belgeler] class BuiltinCapitalize(BuiltinFormatterFunction): name = 'capitalize' arg_count = 1 category = 'String case changes' __doc__ = doc = _('capitalize(val) -- return val capitalized')

def evaluate(self, formatter, kwargs, mi, locals, val): return capitalize(val)

[belgeler] class BuiltinBooksize(BuiltinFormatterFunction): name = 'booksize' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('booksize() -- return value of the size field. ' 'This function works only in the GUI. If you want to use this value ' 'in save-to-disk or send-to-device templates then you ' 'must make a custom "Column built from other columns", use ' 'the function in that column\'s template, and use that ' 'column\'s value in your save/send templates')

def evaluate(self, formatter, kwargs, mi, locals): if hasattr(mi, '_proxy_metadata'): try: v = mi._proxy_metadata.book_size if v is not None: return str(mi._proxy_metadata.book_size) return '' except: pass return '' self.only_in_gui_error()

[belgeler] class BuiltinOndevice(BuiltinFormatterFunction): name = 'ondevice' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('ondevice() -- return Yes if ondevice is set, otherwise return ' 'the empty string. This function works only in the GUI. If you want to ' 'use this value in save-to-disk or send-to-device templates then you ' 'must make a custom "Column built from other columns", use ' 'the function in that column\'s template, and use that ' 'column\'s value in your save/send templates')

def evaluate(self, formatter, kwargs, mi, locals): if hasattr(mi, '_proxy_metadata'): if mi._proxy_metadata.ondevice_col: return _('Yes') return '' self.only_in_gui_error()

[belgeler] class BuiltinAnnotationCount(BuiltinFormatterFunction): name = 'annotation_count' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('annotation_count() -- return the total number of annotations ' 'of all types attached to the current book. ' 'This function works only in the GUI.')

def evaluate(self, formatter, kwargs, mi, locals): c = self.get_database(mi).new_api.annotation_count_for_book(mi.id) return '' if c == 0 else str(c)

[belgeler] class BuiltinIsMarked(BuiltinFormatterFunction): name = 'is_marked' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _("is_marked() -- check whether the book is 'marked' in " "calibre. If it is then return the value of the mark, " "either 'true' or the comma-separated list of named " "marks. Returns '' if the book is not marked.")

def evaluate(self, formatter, kwargs, mi, locals): c = self.get_database(mi).data.get_marked(mi.id) return c if c else ''

[belgeler] class BuiltinSeriesSort(BuiltinFormatterFunction): name = 'series_sort' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('series_sort() -- return the series sort value')

def evaluate(self, formatter, kwargs, mi, locals): if mi.series: langs = mi.languages lang = langs[0] if langs else None return title_sort(mi.series, lang=lang) return ''

[belgeler] class BuiltinHasCover(BuiltinFormatterFunction): name = 'has_cover' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('has_cover() -- return Yes if the book has a cover, ' 'otherwise return the empty string')

def evaluate(self, formatter, kwargs, mi, locals): if mi.has_cover: return _('Yes') return ''

[belgeler] class BuiltinFirstNonEmpty(BuiltinFormatterFunction): name = 'first_non_empty' arg_count = -1 category = 'Iterating over values' __doc__ = doc = _('first_non_empty(value [, value]*) -- ' 'returns the first value that is not empty. If all values are ' 'empty, then the empty string is returned. ' 'You can have as many values as you want.')

def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 while i < len(args): if args[i]: return args[i] i += 1 return ''

[belgeler] class BuiltinAnd(BuiltinFormatterFunction): name = 'and' arg_count = -1 category = 'Boolean' __doc__ = doc = _('and(value [, value]*) -- ' 'returns the string "1" if all values are not empty, otherwise ' 'returns the empty string. This function works well with test or ' 'first_non_empty. You can have as many values as you want. In many ' 'cases the && operator can replace this function.')

def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 while i < len(args): if not args[i]: return '' i += 1 return '1'

[belgeler] class BuiltinOr(BuiltinFormatterFunction): name = 'or' arg_count = -1 category = 'Boolean' __doc__ = doc = _('or(value [, value]*) -- ' 'returns the string "1" if any value is not empty, otherwise ' 'returns the empty string. This function works well with test or ' 'first_non_empty. You can have as many values as you want. In many ' 'cases the || operator can replace this function.')

def evaluate(self, formatter, kwargs, mi, locals, *args): i = 0 while i < len(args): if args[i]: return '1' i += 1 return ''

[belgeler] class BuiltinNot(BuiltinFormatterFunction): name = 'not' arg_count = 1 category = 'Boolean' __doc__ = doc = _('not(value) -- ' 'returns the string "1" if the value is empty, otherwise ' 'returns the empty string. This function works well with test or ' 'first_non_empty. In many cases the ! operator can replace this ' 'function.')

def evaluate(self, formatter, kwargs, mi, locals, val): return '' if val else '1'

[belgeler] class BuiltinListJoin(BuiltinFormatterFunction): name = 'list_join' arg_count = -1 category = 'List manipulation' __doc__ = doc = _("list_join(with_separator, list1, separator1 [, list2, separator2]*) -- " "return a list made by joining the items in the source lists " "(list1, etc) using with_separator between the items in the " "result list. Items in each source list[123...] are separated " "by the associated separator[123...]. A list can contain " "zero values. It can be a field like publisher that is " "single-valued, effectively a one-item list. Duplicates " "are removed using a case-insensitive comparison. Items are " "returned in the order they appear in the source lists. " "If items on lists differ only in letter case then the last " "is used. All separators can be more than one character.\n" "Example:") + "\n\n" + ( " program:\n" " list_join('#@#', $authors, '&', $tags, ',')\n\n") + _( "You can use list_join on the results of previous " "calls to list_join as follows:") + "\n" + ( " program:\n\n" " a = list_join('#@#', $authors, '&', $tags, ',');\n" " b = list_join('#@#', a, '#@#', $#genre, ',', $#people, '&')\n\n") + _( "You can use expressions to generate a list. For example, " "assume you want items for authors and #genre, but " "with the genre changed to the word 'Genre: ' followed by " "the first letter of the genre, i.e. the genre 'Fiction' " "becomes 'Genre: F'. The following will do that:") + "\n" + ( " program:\n" " list_join('#@#', $authors, '&', list_re($#genre, ',', '^(.).*$', 'Genre: \\1'), ',')")

def evaluate(self, formatter, kwargs, mi, locals, with_separator, *args): if len(args) % 2 != 0: raise ValueError( _("Invalid 'List, separator' pairs. Every list must have one " "associated separator"))

# Starting in python 3.7 dicts preserve order so we don't need OrderedDict result = dict() i = 0 while i < len(args): lst = [v.strip() for v in args[i].split(args[i+1]) if v.strip()] result.update({item.lower():item for item in lst}) i += 2 return with_separator.join(result.values())

[belgeler] class BuiltinListUnion(BuiltinFormatterFunction): name = 'list_union' arg_count = 3 category = 'List manipulation' __doc__ = doc = _('list_union(list1, list2, separator) -- ' 'return a list made by merging the items in list1 and list2, ' 'removing duplicate items using a case-insensitive comparison. If ' 'items differ in case, the one in list1 is used. ' 'The items in list1 and list2 are separated by separator, as are ' 'the items in the returned list. Aliases: list_union(), merge_lists()') aliases = ['merge_lists']

def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): res = {icu_lower(l.strip()): l.strip() for l in list2.split(separator) if l.strip()} res.update({icu_lower(l.strip()): l.strip() for l in list1.split(separator) if l.strip()}) if separator == ',': separator = ', ' return separator.join(res.values())

[belgeler] class BuiltinRange(BuiltinFormatterFunction): name = 'range' arg_count = -1 category = 'List manipulation' __doc__ = doc = _("range(start, stop, step, limit) -- " "returns a list of numbers generated by looping over the " "range specified by the parameters start, stop, and step, " "with a maximum length of limit. The first value produced " "is 'start'. Subsequent values next_v are " "current_v+step. The loop continues while " "next_v < stop assuming step is positive, otherwise " "while next_v > stop. An empty list is produced if " "start fails the test: start>=stop if step " "is positive. The limit sets the maximum length of " "the list and has a default of 1000. The parameters " "start, step, and limit are optional. " "Calling range() with one argument specifies stop. " "Two arguments specify start and stop. Three arguments " "specify start, stop, and step. Four " "arguments specify start, stop, step and limit. " "Examples: range(5) -> '0,1,2,3,4'. range(0,5) -> '0,1,2,3,4'. " "range(-1,5) -> '-1,0,1,2,3,4'. range(1,5) -> '1,2,3,4'. " "range(1,5,2) -> '1,3'. range(1,5,2,5) -> '1,3'. " "range(1,5,2,1) -> error(limit exceeded).")

def evaluate(self, formatter, kwargs, mi, locals, *args): limit_val = 1000 start_val = 0 step_val = 1 if len(args) == 1: stop_val = int(args[0] if args[0] and args[0] != 'None' else 0) elif len(args) == 2: start_val = int(args[0] if args[0] and args[0] != 'None' else 0) stop_val = int(args[1] if args[1] and args[1] != 'None' else 0) elif len(args) >= 3: start_val = int(args[0] if args[0] and args[0] != 'None' else 0) stop_val = int(args[1] if args[1] and args[1] != 'None' else 0) step_val = int(args[2] if args[2] and args[2] != 'None' else 0) if len(args) > 3: limit_val = int(args[3] if args[3] and args[3] != 'None' else 0) r = range(start_val, stop_val, step_val) if len(r) > limit_val: raise ValueError( _("{0}: length ({1}) longer than limit ({2})").format( 'range', len(r), str(limit_val))) return ', '.join([str(v) for v in r])

[belgeler] class BuiltinListRemoveDuplicates(BuiltinFormatterFunction): name = 'list_remove_duplicates' arg_count = 2 category = 'List manipulation' __doc__ = doc = _('list_remove_duplicates(list, separator) -- ' 'return a list made by removing duplicate items in the source list. ' 'If items differ only in case, the last of them is returned. ' 'The items in source list are separated by separator, as are ' 'the items in the returned list.')

def evaluate(self, formatter, kwargs, mi, locals, list_, separator): res = {icu_lower(l.strip()): l.strip() for l in list_.split(separator) if l.strip()} if separator == ',': separator = ', ' return separator.join(res.values())

[belgeler] class BuiltinListDifference(BuiltinFormatterFunction): name = 'list_difference' arg_count = 3 category = 'List manipulation' __doc__ = doc = _('list_difference(list1, list2, separator) -- ' 'return a list made by removing from list1 any item found in list2, ' 'using a case-insensitive comparison. The items in list1 and list2 ' 'are separated by separator, as are the items in the returned list.')

def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): l1 = [l.strip() for l in list1.split(separator) if l.strip()] l2 = {icu_lower(l.strip()) for l in list2.split(separator) if l.strip()}

res = [] for i in l1: if icu_lower(i) not in l2 and i not in res: res.append(i) if separator == ',': return ', '.join(res) return separator.join(res)

[belgeler] class BuiltinListIntersection(BuiltinFormatterFunction): name = 'list_intersection' arg_count = 3 category = 'List manipulation' __doc__ = doc = _('list_intersection(list1, list2, separator) -- ' 'return a list made by removing from list1 any item not found in list2, ' 'using a case-insensitive comparison. The items in list1 and list2 ' 'are separated by separator, as are the items in the returned list.')

def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): l1 = [l.strip() for l in list1.split(separator) if l.strip()] l2 = {icu_lower(l.strip()) for l in list2.split(separator) if l.strip()}

res = [] for i in l1: if icu_lower(i) in l2 and i not in res: res.append(i) if separator == ',': return ', '.join(res) return separator.join(res)

[belgeler] class BuiltinListSort(BuiltinFormatterFunction): name = 'list_sort' arg_count = 3 category = 'List manipulation' __doc__ = doc = _('list_sort(list, direction, separator) -- ' 'return list sorted using a case-insensitive sort. If direction is ' 'zero, the list is sorted ascending, otherwise descending. The list items ' 'are separated by separator, as are the items in the returned list.')

def evaluate(self, formatter, kwargs, mi, locals, list1, direction, separator): res = [l.strip() for l in list1.split(separator) if l.strip()] if separator == ',': return ', '.join(sorted(res, key=sort_key, reverse=direction != "0")) return separator.join(sorted(res, key=sort_key, reverse=direction != "0"))

[belgeler] class BuiltinListEquals(BuiltinFormatterFunction): name = 'list_equals' arg_count = 6 category = 'List manipulation' __doc__ = doc = _('list_equals(list1, sep1, list2, sep2, yes_val, no_val) -- ' 'return yes_val if list1 and list2 contain the same items, ' 'otherwise return no_val. The items are determined by splitting ' 'each list using the appropriate separator character (sep1 or ' 'sep2). The order of items in the lists is not relevant. ' 'The comparison is case insensitive.')

def evaluate(self, formatter, kwargs, mi, locals, list1, sep1, list2, sep2, yes_val, no_val): s1 = {icu_lower(l.strip()) for l in list1.split(sep1) if l.strip()} s2 = {icu_lower(l.strip()) for l in list2.split(sep2) if l.strip()} if s1 == s2: return yes_val return no_val

[belgeler] class BuiltinListRe(BuiltinFormatterFunction): name = 'list_re' arg_count = 4 category = 'List manipulation' __doc__ = doc = _('list_re(src_list, separator, include_re, opt_replace) -- ' 'Construct a list by first separating src_list into items using ' 'the separator character. For each item in the list, check if it ' 'matches include_re. If it does, then add it to the list to be ' 'returned. If opt_replace is not the empty string, then apply the ' 'replacement before adding the item to the returned list.')

def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re, opt_replace): l = [l.strip() for l in src_list.split(separator) if l.strip()] res = [] for item in l: if re.search(include_re, item, flags=re.I) is not None: if opt_replace: item = re.sub(include_re, opt_replace, item) for i in [t.strip() for t in item.split(separator) if t.strip()]: if i not in res: res.append(i) if separator == ',': return ', '.join(res) return separator.join(res)

[belgeler] class BuiltinListReGroup(BuiltinFormatterFunction): name = 'list_re_group' arg_count = -1 category = 'List manipulation' __doc__ = doc = _('list_re_group(src_list, separator, include_re, search_re [, group_template]+) -- ' 'Like list_re except replacements are not optional. It ' 'uses re_group(list_item, search_re, group_template, ...) when ' 'doing the replacements on the resulting list.')

def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re, search_re, *args): from calibre.utils.formatter import EvalFormatter l = [l.strip() for l in src_list.split(separator) if l.strip()] res = [] for item in l: def repl(mo): newval = '' if mo and mo.lastindex: for dex in range(0, mo.lastindex): gv = mo.group(dex+1) if gv is None: continue if len(args) > dex: template = args[dex].replace('[[', '{').replace(']]', '}') newval += EvalFormatter().safe_format(template, {'$': gv}, 'EVAL', None, strip_results=False) else: newval += gv return newval if re.search(include_re, item, flags=re.I) is not None: item = re.sub(search_re, repl, item, flags=re.I) for i in [t.strip() for t in item.split(separator) if t.strip()]: if i not in res: res.append(i) if separator == ',': return ', '.join(res) return separator.join(res)

[belgeler] class BuiltinToday(BuiltinFormatterFunction): name = 'today' arg_count = 0 category = 'Date functions' __doc__ = doc = _('today() -- ' 'return a date string for today. This value is designed for use in ' 'format_date or days_between, but can be manipulated like any ' 'other string. The date is in ISO format.')

def evaluate(self, formatter, kwargs, mi, locals): return format_date(now(), 'iso')

[belgeler] class BuiltinDaysBetween(BuiltinFormatterFunction): name = 'days_between' arg_count = 2 category = 'Date functions' __doc__ = doc = _('days_between(date1, date2) -- ' 'return the number of days between date1 and date2. The number is ' 'positive if date1 is greater than date2, otherwise negative. If ' 'either date1 or date2 are not dates, the function returns the ' 'empty string.')

def evaluate(self, formatter, kwargs, mi, locals, date1, date2): try: d1 = parse_date(date1) if d1 == UNDEFINED_DATE: return '' d2 = parse_date(date2) if d2 == UNDEFINED_DATE: return '' except: return '' i = d1 - d2 return '%.1f'%(i.days + (i.seconds/(24.0*60.0*60.0)))

[belgeler] class BuiltinDateArithmetic(BuiltinFormatterFunction): name = 'date_arithmetic' arg_count = -1 category = 'Date functions' __doc__ = doc = _('date_arithmetic(date, calc_spec, fmt) -- ' "Calculate a new date from 'date' using 'calc_spec'. Return the " "new date formatted according to optional 'fmt': if not supplied " "then the result will be in iso format. The calc_spec is a string " "formed by concatenating pairs of 'vW' (valueWhat) where 'v' is a " "possibly-negative number and W is one of the following letters: " "s: add 'v' seconds to 'date' " "m: add 'v' minutes to 'date' " "h: add 'v' hours to 'date' " "d: add 'v' days to 'date' " "w: add 'v' weeks to 'date' " "y: add 'v' years to 'date', where a year is 365 days. " "Example: '1s3d-1m' will add 1 second, add 3 days, and subtract 1 " "minute from 'date'.")

calc_ops = { 's': lambda v: timedelta(seconds=v), 'm': lambda v: timedelta(minutes=v), 'h': lambda v: timedelta(hours=v), 'd': lambda v: timedelta(days=v), 'w': lambda v: timedelta(weeks=v), 'y': lambda v: timedelta(days=v * 365), }

def evaluate(self, formatter, kwargs, mi, locals, date, calc_spec, fmt=None): try: d = parse_date(date) if d == UNDEFINED_DATE: return '' while calc_spec: mo = re.match(r'([-+\d]+)([smhdwy])', calc_spec) if mo is None: raise ValueError( _("{0}: invalid calculation specifier '{1}'").format( 'date_arithmetic', calc_spec)) d += self.calc_ops[mo[2]](int(mo[1])) calc_spec = calc_spec[len(mo[0]):] return format_date(d, fmt if fmt else 'iso') except ValueError as e: raise e except Exception as e: traceback.print_exc() raise ValueError(_("{0}: error: {1}").format('date_arithmetic', str(e)))

[belgeler] class BuiltinLanguageStrings(BuiltinFormatterFunction): name = 'language_strings' arg_count = 2 category = 'Get values from metadata' __doc__ = doc = _('language_strings(lang_codes, localize) -- ' 'return the strings for the language codes passed in lang_codes. ' 'If localize is zero, return the strings in English. If ' 'localize is not zero, return the strings in the language of ' 'the current locale. Lang_codes is a comma-separated list.')

def evaluate(self, formatter, kwargs, mi, locals, lang_codes, localize): retval = [] for c in [c.strip() for c in lang_codes.split(',') if c.strip()]: try: n = calibre_langcode_to_name(c, localize != '0') if n: retval.append(n) except: pass return ', '.join(retval)

[belgeler] class BuiltinLanguageCodes(BuiltinFormatterFunction): name = 'language_codes' arg_count = 1 category = 'Get values from metadata' __doc__ = doc = _('language_codes(lang_strings) -- ' 'return the language codes for the strings passed in lang_strings. ' 'The strings must be in the language of the current locale. ' 'Lang_strings is a comma-separated list.')

def evaluate(self, formatter, kwargs, mi, locals, lang_strings): retval = [] for c in [c.strip() for c in lang_strings.split(',') if c.strip()]: try: cv = canonicalize_lang(c) if cv: retval.append(canonicalize_lang(cv)) except: pass return ', '.join(retval)

[belgeler] class BuiltinCurrentLibraryName(BuiltinFormatterFunction): name = 'current_library_name' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('current_library_name() -- ' 'return the last name on the path to the current calibre library. ' 'This function can be called in template program mode using the ' 'template "{:\'current_library_name()\'}".')

def evaluate(self, formatter, kwargs, mi, locals): from calibre.library import current_library_name return current_library_name()

[belgeler] class BuiltinCurrentLibraryPath(BuiltinFormatterFunction): name = 'current_library_path' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('current_library_path() -- ' 'return the path to the current calibre library. This function can ' 'be called in template program mode using the template ' '"{:\'current_library_path()\'}".')

def evaluate(self, formatter, kwargs, mi, locals): from calibre.library import current_library_path return current_library_path()

[belgeler] class BuiltinFinishFormatting(BuiltinFormatterFunction): name = 'finish_formatting' arg_count = 4 category = 'Formatting values' __doc__ = doc = _('finish_formatting(val, fmt, prefix, suffix) -- apply the ' 'format, prefix, and suffix to a value in the same way as ' 'done in a template like `{series_index:05.2f| - |- }`. For ' 'example, the following program produces the same output ' 'as the above template: ' 'program: finish_formatting(field("series_index"), "05.2f", " - ", " - ")')

def evaluate(self, formatter, kwargs, mi, locals_, val, fmt, prefix, suffix): if not val: return val return prefix + formatter._do_format(val, fmt) + suffix

[belgeler] class BuiltinVirtualLibraries(BuiltinFormatterFunction): name = 'virtual_libraries' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('virtual_libraries() -- return a comma-separated list of ' 'Virtual libraries that contain this book. This function ' 'works only in the GUI. If you want to use these values ' 'in save-to-disk or send-to-device templates then you ' 'must make a custom "Column built from other columns", use ' 'the function in that column\'s template, and use that ' 'column\'s value in your save/send templates')

def evaluate(self, formatter, kwargs, mi, locals_): db = self.get_database(mi) try: a = db.data.get_virtual_libraries_for_books((mi.id,)) return ', '.join(a[mi.id]) except ValueError as v: return str(v)

[belgeler] class BuiltinCurrentVirtualLibraryName(BuiltinFormatterFunction): name = 'current_virtual_library_name' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('current_virtual_library_name() -- ' 'return the name of the current virtual library if there is one, ' 'otherwise the empty string. Library name case is preserved. ' 'Example: "program: current_virtual_library_name()".')

def evaluate(self, formatter, kwargs, mi, locals): return self.get_database(mi).data.get_base_restriction_name()

[belgeler] class BuiltinUserCategories(BuiltinFormatterFunction): name = 'user_categories' arg_count = 0 category = 'Get values from metadata' __doc__ = doc = _('user_categories() -- return a comma-separated list of ' 'the user categories that contain this book. This function ' 'works only in the GUI. If you want to use these values ' 'in save-to-disk or send-to-device templates then you ' 'must make a custom "Column built from other columns", use ' 'the function in that column\'s template, and use that ' 'column\'s value in your save/send templates')

def evaluate(self, formatter, kwargs, mi, locals_): if hasattr(mi, '_proxy_metadata'): cats = {k for k, v in iteritems(mi._proxy_metadata.user_categories) if v} cats = sorted(cats, key=sort_key) return ', '.join(cats) self.only_in_gui_error()

[belgeler] class BuiltinTransliterate(BuiltinFormatterFunction): name = 'transliterate' arg_count = 1 category = 'String manipulation' __doc__ = doc = _('transliterate(a) -- Returns a string in a latin alphabet ' 'formed by approximating the sound of the words in the ' 'source string. For example, if the source is "{0}"' ' the function returns "{1}".').format( "Фёдор Миха́йлович Достоевский", 'Fiodor Mikhailovich Dostoievskii')

def evaluate(self, formatter, kwargs, mi, locals, source): from calibre.utils.filenames import ascii_text return ascii_text(source)

[belgeler] class BuiltinGetLink(BuiltinFormatterFunction): name = 'get_link' arg_count = 2 category = 'Template database functions' __doc__ = doc = _("get_link(field_name, field_value) -- fetch the link for " "field 'field_name' with value 'field_value'. If there is " "no attached link, return ''. Example: " "get_link('tags', 'Fiction') returns the link attached to " "the tag 'Fiction'.")

def evaluate(self, formatter, kwargs, mi, locals, field_name, field_value): db = self.get_database(mi).new_api try: link = None item_id = db.get_item_id(field_name, field_value) if item_id is not None: link = db.link_for(field_name, item_id) return link if link is not None else '' except Exception as e: traceback.print_exc() raise ValueError(e)

[belgeler] class BuiltinAuthorLinks(BuiltinFormatterFunction): name = 'author_links' arg_count = 2 category = 'Get values from metadata' __doc__ = doc = _('author_links(val_separator, pair_separator) -- returns ' 'a string containing a list of authors and that author\'s ' 'link values in the ' 'form author1 val_separator author1link pair_separator ' 'author2 val_separator author2link etc. An author is ' 'separated from its link value by the val_separator string ' 'with no added spaces. author:linkvalue pairs are separated ' 'by the pair_separator string argument with no added spaces. ' 'It is up to you to choose separator strings that do ' 'not occur in author names or links. An author is ' 'included even if the author link is empty.')

def evaluate(self, formatter, kwargs, mi, locals, val_sep, pair_sep): if hasattr(mi, '_proxy_metadata'): link_data = mi._proxy_metadata.link_maps if not link_data: return '' link_data = link_data.get('authors') if not link_data: return '' names = sorted(link_data.keys(), key=sort_key) return pair_sep.join(n + val_sep + link_data[n] for n in names) self.only_in_gui_error()

[belgeler] class BuiltinAuthorSorts(BuiltinFormatterFunction): name = 'author_sorts' arg_count = 1 category = 'Get values from metadata' __doc__ = doc = _('author_sorts(val_separator) -- returns a string ' 'containing a list of author\'s sort values for the ' 'authors of the book. The sort is the one in the author ' 'metadata (different from the author_sort in books). The ' 'returned list has the form author sort 1 val_separator ' 'author sort 2 etc. The author sort values in this list ' 'are in the same order as the authors of the book. If ' 'you want spaces around val_separator then include them ' 'in the separator string')

def evaluate(self, formatter, kwargs, mi, locals, val_sep): sort_data = mi.author_sort_map if not sort_data: return '' names = [sort_data.get(n) for n in mi.authors if n.strip()] return val_sep.join(n for n in names)

[belgeler] class BuiltinConnectedDeviceName(BuiltinFormatterFunction): name = 'connected_device_name' arg_count = 1 category = 'Get values from metadata' __doc__ = doc = _("connected_device_name(storage_location) -- if a device is " "connected then return the device name, otherwise return " "the empty string. Each storage location on a device can " "have a different name. The location names are 'main', " "'carda' and 'cardb'. This function works only in the GUI.")

def evaluate(self, formatter, kwargs, mi, locals, storage_location): # We can't use get_database() here because we need the device manager. # In other words, the function really does need the GUI with suppress(Exception): # Do the import here so that we don't entangle the GUI when using # command line functions from calibre.gui2.ui import get_gui info = get_gui().device_manager.get_current_device_information() if info is None: return '' try: if storage_location not in {'main', 'carda', 'cardb'}: raise ValueError( _('connected_device_name: invalid storage location "{}"' .format(storage_location))) info = info['info'][4] if storage_location not in info: return '' return info[storage_location]['device_name'] except Exception: traceback.print_exc() raise self.only_in_gui_error()

[belgeler] class BuiltinConnectedDeviceUUID(BuiltinFormatterFunction): name = 'connected_device_uuid' arg_count = 1 category = 'Get values from metadata' __doc__ = doc = _("connected_device_uuid(storage_location) -- if a device is " "connected then return the device uuid (unique id), " "otherwise return the empty string. Each storage location " "on a device has a different uuid. The location names are " "'main', 'carda' and 'cardb'. This function works only in " "the GUI.")

def evaluate(self, formatter, kwargs, mi, locals, storage_location): # We can't use get_database() here because we need the device manager. # In other words, the function really does need the GUI with suppress(Exception): # Do the import here so that we don't entangle the GUI when using # command line functions from calibre.gui2.ui import get_gui info = get_gui().device_manager.get_current_device_information() if info is None: return '' try: if storage_location not in {'main', 'carda', 'cardb'}: raise ValueError( _('connected_device_name: invalid storage location "{}"' .format(storage_location))) info = info['info'][4] if storage_location not in info: return '' return info[storage_location]['device_store_uuid'] except Exception: traceback.print_exc() raise self.only_in_gui_error()

[belgeler] class BuiltinCheckYesNo(BuiltinFormatterFunction): name = 'check_yes_no' arg_count = 4 category = 'If-then-else' __doc__ = doc = _('check_yes_no(field_name, is_undefined, is_false, is_true) ' '-- checks the value of the yes/no field named by the ' 'lookup key field_name for a value specified by the ' 'parameters, returning "yes" if a match is found, otherwise ' 'returning an empty string. Set the parameter is_undefined, ' 'is_false, or is_true to 1 (the number) to check that ' 'condition, otherwise set it to 0. Example: ' 'check_yes_no("#bool", 1, 0, 1) returns "yes" if the ' 'yes/no field "#bool" is either undefined (neither True ' 'nor False) or True. More than one of is_undefined, ' 'is_false, or is_true can be set to 1. This function ' 'is usually used by the test() or is_empty() functions.')

def evaluate(self, formatter, kwargs, mi, locals, field, is_undefined, is_false, is_true): # 'field' is a lookup name, not a value if field not in self.get_database(mi).field_metadata: raise ValueError(_("The column {} doesn't exist").format(field)) res = getattr(mi, field, None) if res is None: if is_undefined == '1': return 'Yes' return "" if not isinstance(res, bool): raise ValueError(_('check_yes_no requires the field be a Yes/No custom column')) if is_false == '1' and not res: return 'Yes' if is_true == '1' and res: return 'Yes' return ""

[belgeler] class BuiltinRatingToStars(BuiltinFormatterFunction): name = 'rating_to_stars' arg_count = 2 category = 'Formatting values' __doc__ = doc = _('rating_to_stars(value, use_half_stars) ' '-- Returns the rating as string of star characters. ' 'The value is a number between 0 and 5. Set use_half_stars ' 'to 1 if you want half star characters for custom ratings ' 'columns that support non-integer ratings, for example 2.5.')

def evaluate(self, formatter, kwargs, mi, locals, value, use_half_stars): if not value: return '' err_msg = _('The rating must be a number between 0 and 5') try: v = float(value) * 2 except: raise ValueError(err_msg) if v < 0 or v > 10: raise ValueError(err_msg) from calibre.ebooks.metadata import rating_to_stars return rating_to_stars(v, use_half_stars == '1')

[belgeler] class BuiltinSwapAroundArticles(BuiltinFormatterFunction): name = 'swap_around_articles' arg_count = 2 category = 'String manipulation' __doc__ = doc = _('swap_around_articles(val, separator) ' '-- returns the val with articles moved to the end. ' 'The value can be a list, in which case each member ' 'of the list is processed. If the value is a list then ' 'you must provide the list value separator. If no ' 'separator is provided then the value is treated as ' 'being a single value, not a list.')

def evaluate(self, formatter, kwargs, mi, locals, val, separator): if not val: return '' if not separator: return title_sort(val).replace(',', ';') result = [] try: for v in [x.strip() for x in val.split(separator)]: result.append(title_sort(v).replace(',', ';')) except: traceback.print_exc() return separator.join(sorted(result, key=sort_key))

[belgeler] class BuiltinArguments(BuiltinFormatterFunction): name = 'arguments' arg_count = -1 category = 'Other' __doc__ = doc = _('arguments(id[=expression] [, id[=expression]]*) ' '-- Used in a stored template to retrieve the arguments ' 'passed in the call. It both declares and initializes ' 'local variables, effectively parameters. The variables ' 'are positional; they get the value of the parameter given ' 'in the call in the same position. If the corresponding ' 'parameter is not provided in the call then arguments ' 'assigns that variable the provided default value. If ' 'there is no default value then the variable is set to ' 'the empty string.')

def evaluate(self, formatter, kwargs, mi, locals, *args): # The arguments function is implemented in-line in the formatter raise NotImplementedError()

[belgeler] class BuiltinGlobals(BuiltinFormatterFunction): name = 'globals' arg_count = -1 category = 'Other' __doc__ = doc = _('globals(id[=expression] [, id[=expression]]*) ' '-- Retrieves "global variables" that can be passed into ' 'the formatter. It both declares and initializes local ' 'variables with the names of the global variables passed ' 'in. If the corresponding variable is not provided in ' 'the passed-in globals then it assigns that variable the ' 'provided default value. If there is no default value ' 'then the variable is set to the empty string.')

def evaluate(self, formatter, kwargs, mi, locals, *args): # The globals function is implemented in-line in the formatter raise NotImplementedError()

[belgeler] class BuiltinSetGlobals(BuiltinFormatterFunction): name = 'set_globals' arg_count = -1 category = 'other' __doc__ = doc = _('set_globals(id[=expression] [, id[=expression]]*) ' '-- Sets "global variables" that can be passed into ' 'the formatter. The globals are given the name of the id ' 'passed in. The value of the id is used unless an ' 'expression is provided.')

def evaluate(self, formatter, kwargs, mi, locals, *args): # The globals function is implemented in-line in the formatter raise NotImplementedError()

[belgeler] class BuiltinFieldExists(BuiltinFormatterFunction): name = 'field_exists' arg_count = 1 category = 'If-then-else' __doc__ = doc = _('field_exists(field_name) -- checks if a field ' '(column) named field_name exists, returning ' "'1' if so and '' if not.")

def evaluate(self, formatter, kwargs, mi, locals, field_name): if field_name.lower() in mi.all_field_keys(): return '1' return ''

[belgeler] class BuiltinCharacter(BuiltinFormatterFunction): name = 'character' arg_count = 1 category = 'String manipulation' __doc__ = doc = _('character(character_name) -- returns the ' 'character named by character_name. For example, ' r"character('newline') returns a newline character ('\n'). " "The supported character names are 'newline', 'return', " "'tab', and 'backslash'.")

def evaluate(self, formatter, kwargs, mi, locals, character_name): # The globals function is implemented in-line in the formatter raise NotImplementedError()

[belgeler] class BuiltinToHex(BuiltinFormatterFunction): name = 'to_hex' arg_count = 1 category = 'String manipulation' __doc__ = doc = _('to_hex(val) -- returns the string encoded in hex. ' 'This is useful when constructing calibre URLs.')

def evaluate(self, formatter, kwargs, mi, locals, val): return val.encode().hex()

[belgeler] class BuiltinUrlsFromIdentifiers(BuiltinFormatterFunction): name = 'urls_from_identifiers' arg_count = 2 category = 'Formatting values' __doc__ = doc = _('urls_from_identifiers(identifiers, sort_results) -- given ' 'a comma-separated list of identifiers, where an identifier ' 'is a colon-separated pair of values (name:id_value), returns a ' 'comma-separated list of HTML URLs generated from the ' 'identifiers. The list not sorted if sort_results is 0 ' '(character or number), otherwise it is sorted alphabetically ' 'by the identifier name. The URLs are generated in the same way ' 'as the built-in identifiers column when shown in Book details.')

def evaluate(self, formatter, kwargs, mi, locals, identifiers, sort_results): from calibre.ebooks.metadata.sources.identify import urls_from_identifiers try: v = {} for id_ in identifiers.split(','): if id_: pair = id_.split(':', maxsplit=1) if len(pair) == 2: l = pair[0].strip() r = pair[1].strip() if l and r: v[l] = r urls = urls_from_identifiers(v, sort_results=str(sort_results) != '0') p = prepare_string_for_xml a = partial(prepare_string_for_xml, attribute=True) links = [f'<a href="{a(url)}" title="{a(id_typ)}:{a(id_val)}">{p(name)}</a>' for name, id_typ, id_val, url in urls] return ', '.join(links) except Exception as e: return str(e)

[belgeler] class BuiltinBookCount(BuiltinFormatterFunction): name = 'book_count' arg_count = 2 category = 'Template database functions' __doc__ = doc = _('book_count(query, use_vl) -- returns the count of ' 'books found by searching for query. If use_vl is ' '0 (zero) then virtual libraries are ignored. This ' 'function can be used only in the GUI.')

def evaluate(self, formatter, kwargs, mi, locals, query, use_vl): from calibre.db.fields import rendering_composite_name if (not tweaks.get('allow_template_database_functions_in_composites', False) and formatter.global_vars.get(rendering_composite_name, None)): raise ValueError(_('The book_count() function cannot be used in a composite column')) db = self.get_database(mi) try: ids = db.search_getting_ids(query, None, use_virtual_library=use_vl != '0') return len(ids) except Exception: traceback.print_exc()

[belgeler] class BuiltinBookValues(BuiltinFormatterFunction): name = 'book_values' arg_count = 4 category = 'Template database functions' __doc__ = doc = _('book_values(column, query, sep, use_vl) -- returns a list ' 'of the values contained in the column "column", separated ' 'by "sep", in the books found by searching for "query". ' 'If use_vl is 0 (zero) then virtual libraries are ignored. ' 'This function can be used only in the GUI.')

def evaluate(self, formatter, kwargs, mi, locals, column, query, sep, use_vl): from calibre.db.fields import rendering_composite_name if (not tweaks.get('allow_template_database_functions_in_composites', False) and formatter.global_vars.get(rendering_composite_name, None)): raise ValueError(_('The book_values() function cannot be used in a composite column')) db = self.get_database(mi) if column not in db.field_metadata: raise ValueError(_("The column {} doesn't exist").format(column)) try: ids = db.search_getting_ids(query, None, use_virtual_library=use_vl != '0') s = set() for id_ in ids: f = db.new_api.get_proxy_metadata(id_).get(column, None) if isinstance(f, (tuple, list)): s.update(f) elif f is not None: s.add(str(f)) return sep.join(s) except Exception as e: raise ValueError(e)[belgeler] class BuiltinHasExtraFiles(BuiltinFormatterFunction): name = 'has_extra_files' arg_count = -1 category = 'Template database functions' __doc__ = doc = _("has_extra_files([pattern]) -- returns the count of extra " "files, otherwise '' (the empty string). " "If the optional parameter 'pattern' (a regular expression) " "is supplied then the list is filtered to files that match " "pattern before the files are counted. The pattern match is " "case insensitive. " 'This function can be used only in the GUI.')

def evaluate(self, formatter, kwargs, mi, locals, *args): if len(args) > 1: raise ValueError(_('Incorrect number of arguments for function {0}').format('has_extra_files')) pattern = args[0] if len(args) == 1 else None db = self.get_database(mi).new_api try: files = tuple(f.relpath.partition('/')[-1] for f in db.list_extra_files(mi.id, use_cache=True, pattern=DATA_FILE_PATTERN)) if pattern: r = re.compile(pattern, re.IGNORECASE) files = tuple(filter(r.search, files)) return len(files) if len(files) > 0 else '' except Exception as e: traceback.print_exc() raise ValueError(e)[belgeler] class BuiltinExtraFileNames(BuiltinFormatterFunction): name = 'extra_file_names' arg_count = -1 category = 'Template database functions' __doc__ = doc = _("extra_file_names(sep [, pattern]) -- returns a sep-separated " "list of extra files in the book's '{}/' folder. If the " "optional parameter 'pattern', a regular expression, is " "supplied then the list is filtered to files that match pattern. " "The pattern match is case insensitive. " 'This function can be used only in the GUI.').format(DATA_DIR_NAME)

def evaluate(self, formatter, kwargs, mi, locals, sep, *args): if len(args) > 1: raise ValueError(_('Incorrect number of arguments for function {0}').format('has_extra_files')) pattern = args[0] if len(args) == 1 else None db = self.get_database(mi).new_api try: files = tuple(f.relpath.partition('/')[-1] for f in db.list_extra_files(mi.id, use_cache=True, pattern=DATA_FILE_PATTERN)) if pattern: r = re.compile(pattern, re.IGNORECASE) files = tuple(filter(r.search, files)) return sep.join(files) except Exception as e: traceback.print_exc() raise ValueError(e)

[belgeler] class BuiltinExtraFileSize(BuiltinFormatterFunction): name = 'extra_file_size' arg_count = 1 category = 'Template database functions' __doc__ = doc = _("extra_file_size(file_name) -- returns the size in bytes of " "the extra file 'file_name' in the book's '{}/' folder if " "it exists, otherwise -1." 'This function can be used only in the GUI.').format(DATA_DIR_NAME)

def evaluate(self, formatter, kwargs, mi, locals, file_name): db = self.get_database(mi).new_api try: q = posixpath.join(DATA_DIR_NAME, file_name) for f in db.list_extra_files(mi.id, use_cache=True, pattern=DATA_FILE_PATTERN): if f.relpath == q: return str(f.stat_result.st_size) return str(-1) except Exception as e: traceback.print_exc() raise ValueError(e)

[belgeler] class BuiltinExtraFileModtime(BuiltinFormatterFunction): name = 'extra_file_modtime' arg_count = 2 category = 'Template database functions' __doc__ = doc = _("extra_file_modtime(file_name, format_string) -- returns the " "modification time of the extra file 'file_name' in the " "book's '{}/' folder if it exists, otherwise -1.0. The " "modtime is formatted according to 'format_string' " "(see format_date()). If 'format_string' is empty, returns " "the modtime as the floating point number of seconds since " "the epoch. The epoch is OS dependent. " "This function can be used only in the GUI.").format(DATA_DIR_NAME)

def evaluate(self, formatter, kwargs, mi, locals, file_name, format_string): db = self.get_database(mi).new_api try: q = posixpath.join(DATA_DIR_NAME, file_name) for f in db.list_extra_files(mi.id, use_cache=True, pattern=DATA_FILE_PATTERN): if f.relpath == q: val = f.stat_result.st_mtime if format_string: return format_date(datetime.fromtimestamp(val), format_string) return str(val) return str(1.0) except Exception as e: traceback.print_exc() raise ValueError(e)

[belgeler] class BuiltinGetNote(BuiltinFormatterFunction): name = 'get_note' arg_count = 3 category = 'Template database functions' __doc__ = doc = _("get_note(field_name, field_value, plain_text) -- fetch the " "note for field 'field_name' with value 'field_value'. If " "'plain_text' is empty, return the note's HTML. If 'plain_text' " "is non-empty, return the note's plain text. If the note " "doesn't exist, return '' in both cases. Example: " "get_note('tags', 'Fiction', '') returns the HTML of the " "note attached to the tag 'Fiction'.")

def evaluate(self, formatter, kwargs, mi, locals, field_name, field_value, plain_text): db = self.get_database(mi).new_api try: note = '' item_id = db.get_item_id(field_name, field_value) if item_id is not None: note_data = db.notes_data_for(field_name, item_id) if note_data is not None: if plain_text == '1': return note['searchable_text'].partition('\n')[2] # Return the full HTML of the note, including all images as # data: URLs. Reason: non-exported note html contains # "calres://" URLs for images. These images won't render # outside the context of the library where the note "lives". # For example, they don't work in book jackets and book # details from a different library. They also don't work in # tooltips.

# This code depends on the note being wrapped in <body> tags # by parse_html. The body is changed to a <div>. That means # we often end up with <div><div> or some such, but that is # OK root = parse_html(note_data['doc']) # There should be only one <body> root = root.xpath('//body')[0] # Change the body to a div root.tag = 'div' # Expand all the resources in the note root = expand_note_resources(root, db.get_notes_resource) note = html.tostring(root, encoding='unicode') return note except Exception as e: traceback.print_exc() raise ValueError(e)

[belgeler] class BuiltinHasNote(BuiltinFormatterFunction): name = 'has_note' arg_count = 2 category = 'Template database functions' __doc__ = doc = _("has_note(field_name, field_value) -- return '1' " "if the value 'field_value' in the field 'field_name' " "has an attached note, '' otherwise. Example: " "has_note('tags', 'Fiction') returns '1' if the tag " "'fiction' has an attached note, '' otherwise.")

def evaluate(self, formatter, kwargs, mi, locals, field_name, field_value): db = self.get_database(mi).new_api note = None try: item_id = db.get_item_id(field_name, field_value) if item_id is not None: note = db.notes_data_for(field_name, item_id) except Exception as e: traceback.print_exc() raise ValueError(e) return '1' if note is not None else ''

_formatter_builtins = [ BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(), BuiltinAssign(), BuiltinAuthorLinks(), BuiltinAuthorSorts(), BuiltinBookCount(), BuiltinBookValues(), BuiltinBooksize(), BuiltinCapitalize(), BuiltinCharacter(), BuiltinCheckYesNo(), BuiltinCeiling(), BuiltinCmp(), BuiltinConnectedDeviceName(), BuiltinConnectedDeviceUUID(), BuiltinContains(), BuiltinCount(), BuiltinCurrentLibraryName(), BuiltinCurrentLibraryPath(), BuiltinCurrentVirtualLibraryName(), BuiltinDateArithmetic(), BuiltinDaysBetween(), BuiltinDivide(), BuiltinEval(), BuiltinExtraFileNames(), BuiltinExtraFileSize(), BuiltinExtraFileModtime(), BuiltinFirstNonEmpty(), BuiltinField(), BuiltinFieldExists(), BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(), BuiltinFormatDate(), BuiltinFormatDateField(), BuiltinFormatNumber(), BuiltinFormatsModtimes(), BuiltinFormatsPaths(), BuiltinFormatsSizes(), BuiltinFractionalPart(), BuiltinGetLink(), BuiltinGetNote(), BuiltinGlobals(), BuiltinHasCover(), BuiltinHasExtraFiles(), BuiltinHasNote(), BuiltinHumanReadable(), BuiltinIdentifierInList(), BuiltinIfempty(), BuiltinLanguageCodes(), BuiltinLanguageStrings(), BuiltinInList(), BuiltinIsMarked(), BuiltinListCountMatching(), BuiltinListDifference(), BuiltinListEquals(), BuiltinListIntersection(), BuiltinListitem(), BuiltinListJoin(), BuiltinListRe(), BuiltinListReGroup(), BuiltinListRemoveDuplicates(), BuiltinListSort(), BuiltinListSplit(), BuiltinListUnion(),BuiltinLookup(), BuiltinLowercase(), BuiltinMod(), BuiltinMultiply(), BuiltinNot(), BuiltinOndevice(), BuiltinOr(), BuiltinPrint(), BuiltinRatingToStars(), BuiltinRange(), BuiltinRawField(), BuiltinRawList(), BuiltinRe(), BuiltinReGroup(), BuiltinRound(), BuiltinSelect(), BuiltinSeriesSort(), BuiltinSetGlobals(), BuiltinShorten(), BuiltinStrcat(), BuiltinStrcatMax(), BuiltinStrcmp(), BuiltinStrcmpcase(), BuiltinStrInList(), BuiltinStrlen(), BuiltinSubitems(), BuiltinSublist(),BuiltinSubstr(), BuiltinSubtract(), BuiltinSwapAroundArticles(), BuiltinSwapAroundComma(), BuiltinSwitch(), BuiltinSwitchIf(), BuiltinTemplate(), BuiltinTest(), BuiltinTitlecase(), BuiltinToday(), BuiltinToHex(), BuiltinTransliterate(), BuiltinUppercase(), BuiltinUrlsFromIdentifiers(), BuiltinUserCategories(), BuiltinVirtualLibraries(), BuiltinAnnotationCount() ]

class FormatterUserFunction(FormatterFunction):

def __init__(self, name, doc, arg_count, program_text, object_type): self.object_type = object_type self.name = name self.doc = doc self.arg_count = arg_count self.program_text = program_text self.cached_compiled_text = None # Keep this for external code compatibility. Set it to True if we have a # python template function, otherwise false. This might break something # if the code depends on stored templates being in GPM. self.is_python = True if object_type is StoredObjectType.PythonFunction else False def to_pref(self): return [self.name, self.doc, self.arg_count, self.program_text]

tabs = re.compile(r'^\t*')

def function_object_type(thing): # 'thing' can be a preference instance, program text, or an already-compiled function if isinstance(thing, FormatterUserFunction): return thing.object_type if isinstance(thing, list): text = thing[3] else: text = thing if text.startswith('def'): return StoredObjectType.PythonFunction if text.startswith('program'): return StoredObjectType.StoredGPMTemplate if text.startswith('python'): return StoredObjectType.StoredPythonTemplate raise ValueError('Unknown program type in formatter function pref')

def function_pref_name(pref): return pref[0]

def compile_user_function(name, doc, arg_count, eval_func): typ = function_object_type(eval_func) if typ is not StoredObjectType.PythonFunction: return FormatterUserFunction(name, doc, arg_count, eval_func, typ)

def replace_func(mo): return mo.group().replace('\t', ' ')

func = ' ' + '\n '.join([tabs.sub(replace_func, line) for line in eval_func.splitlines()]) prog = ''' from calibre.utils.formatter_functions import FormatterUserFunction from calibre.utils.formatter_functions import formatter_functions class UserFunction(FormatterUserFunction): ''' + func locals_ = {} if DEBUG and tweaks.get('enable_template_debug_printing', False): print(prog) exec(prog, locals_) cls = locals_['UserFunction'](name, doc, arg_count, eval_func, typ) return cls def compile_user_template_functions(funcs): compiled_funcs = {} for func in funcs: try: # Force a name conflict to test the logic # if func[0] == 'myFunc2': # func[0] = 'myFunc3'

# Compile the function so that the tab processing is done on the # source. This helps ensure that if the function already is defined # then white space differences don't cause them to compare differently cls = compile_user_function(*func) cls.object_type = function_object_type(func) compiled_funcs[cls.name] = cls except Exception: try: func_name = func[0] except Exception: func_name = 'Unknown' prints('**** Compilation errors in user template function "%s" ****' % func_name) traceback.print_exc(limit=10) prints('**** End compilation errors in %s "****"' % func_name) return compiled_funcs def load_user_template_functions(library_uuid, funcs, precompiled_user_functions=None): unload_user_template_functions(library_uuid) if precompiled_user_functions: compiled_funcs = precompiled_user_functions else: compiled_funcs = compile_user_template_functions(funcs) formatter_functions().register_functions(library_uuid, list(compiled_funcs.values()))

def unload_user_template_functions(library_uuid): formatter_functions().unregister_functions(library_uuid)

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Gezinti

- modülleri
- Başla »
- Modül kodu »
- calibre.web.feeds.news

calibre.web.feeds.news öğesinin kaynak kodu

__license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' ''' Defines various abstract base classes that can be subclassed to create powerful news fetching recipes. ''' __docformat__ = "restructuredtext en"

import io import os import re import sys import time import traceback from collections import defaultdict from contextlib import closing from urllib.parse import urlparse, urlsplit from calibre import __appname__, as_unicode, browser, force_unicode, iswindows, preferred_encoding, random_user_agent, strftime from calibre.ebooks.BeautifulSoup import BeautifulSoup, CData, NavigableString, Tag from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.toc import TOC from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.date import now as nowf from calibre.utils.icu import numeric_sort_key from calibre.utils.img import add_borders_to_image, image_to_data, save_cover_data_to from calibre.utils.localization import _, canonicalize_lang, ngettext from calibre.utils.logging import ThreadSafeWrapper from calibre.utils.threadpool import NoResultsPending, ThreadPool, WorkRequest from calibre.web import Recipe from calibre.web.feeds import Feed, feed_from_xml, feeds_from_index, templates from calibre.web.fetch.simple import AbortArticle, RecursiveFetcher from calibre.web.fetch.simple import option_parser as web2disk_option_parser from calibre.web.fetch.utils import prepare_masthead_image from polyglot.builtins import string_or_bytes def classes(classes): q = frozenset(classes.split(' ')) return dict(attrs={ 'class': lambda x: x and frozenset(x.split()).intersection(q)})

def prefixed_classes(classes): q = frozenset(classes.split(' '))

def matcher(x): if x: for candidate in frozenset(x.split()): for x in q: if candidate.startswith(x): return True return False return {'attrs': {'class': matcher}}

class LoginFailed(ValueError): pass class DownloadDenied(ValueError): pass

[belgeler] class BasicNewsRecipe(Recipe): ''' Base class that contains logic needed in all recipes. By overriding progressively more of the functionality in this class, you can make progressively more customized/powerful recipes. For a tutorial introduction to creating recipes, see :doc:`news`. '''

#: The title to use for the e-book title = _('Unknown News Source')

#: A couple of lines that describe the content this recipe downloads. #: This will be used primarily in a GUI that presents a list of recipes. description = ''

#: The author of this recipe __author__ = __appname__

#: Minimum calibre version needed to use this recipe requires_version = (0, 6, 0)

#: The language that the news is in. Must be an ISO-639 code either #: two or three characters long language = 'und'

#: Maximum number of articles to download from each feed. This is primarily #: useful for feeds that don't have article dates. For most feeds, you should #: use :attr:`BasicNewsRecipe.oldest_article` max_articles_per_feed = 100

#: Oldest article to download from this news source. In days. oldest_article = 7.0

#: Number of levels of links to follow on article webpages recursions = 0

#: The default delay between consecutive downloads in seconds. The argument may be a #: floating point number to indicate a more precise time. See :meth:`get_url_specific_delay` #: to implement per URL delays. delay = 0

#: Publication type #: Set to newspaper, magazine or blog. If set to None, no publication type #: metadata will be written to the opf file. publication_type = 'unknown'

#: Number of simultaneous downloads. Set to 1 if the server is picky. #: Automatically reduced to 1 if :attr:`BasicNewsRecipe.delay` > 0 simultaneous_downloads = 5

#: Timeout for fetching files from server in seconds timeout = 120.0

#: The format string for the date shown on the first page. #: By default: Day_Name, Day_Number Month_Name Year timefmt = ' [%a, %d %b %Y]'

#: List of feeds to download. #: Can be either ``[url1, url2, ...]`` or ``[('title1', url1), ('title2', url2),...]`` feeds = None

#: Max number of characters in the short description summary_length = 500

#: Convenient flag to disable loading of stylesheets for websites #: that have overly complex stylesheets unsuitable for conversion #: to e-book formats. #: If True stylesheets are not downloaded and processed no_stylesheets = False

#: Convenient flag to strip all JavaScript tags from the downloaded HTML remove_javascript = True

#: If True the GUI will ask the user for a username and password #: to use while downloading. #: If set to "optional" the use of a username and password becomes optional needs_subscription = False

#: If True the navigation bar is center aligned, otherwise it is left aligned center_navbar = True

#: Specify an override encoding for sites that have an incorrect #: charset specification. The most common being specifying ``latin1`` and #: using ``cp1252``. If None, try to detect the encoding. If it is a #: callable, the callable is called with two arguments: The recipe object #: and the source to be decoded. It must return the decoded source. encoding = None

#: Normally we try to guess if a feed has full articles embedded in it #: based on the length of the embedded content. If `None`, then the #: default guessing is used. If `True` then the we always assume the feeds has #: embedded content and if `False` we always assume the feed does not have #: embedded content. use_embedded_content = None

#: Set to True and implement :meth:`get_obfuscated_article` to handle #: websites that try to make it difficult to scrape content. articles_are_obfuscated = False

#: Reverse the order of articles in each feed reverse_article_order = False

#: Automatically extract all the text from downloaded article pages. Uses #: the algorithms from the readability project. Setting this to True, means #: that you do not have to worry about cleaning up the downloaded HTML #: manually (though manual cleanup will always be superior). auto_cleanup = False

#: Specify elements that the auto cleanup algorithm should never remove. #: The syntax is a XPath expression. For example:: #: #: auto_cleanup_keep = '//div[@id="article-image"]' will keep all divs with #: id="article-image" #: auto_cleanup_keep = '//*[@class="important"]' will keep all elements #: with class="important" #: auto_cleanup_keep = '//div[@id="article-image"]|//span[@class="important"]' #: will keep all divs with id="article-image" and spans #: with class="important" #: auto_cleanup_keep = None

#: Specify any extra :term:`CSS` that should be added to downloaded :term:`HTML` files. #: It will be inserted into `<style>` tags, just before the closing #: `</head>` tag thereby overriding all :term:`CSS` except that which is #: declared using the style attribute on individual :term:`HTML` tags. #: Note that if you want to programmatically generate the extra_css override #: the :meth:`get_extra_css()` method instead. #: For example:: #: #: extra_css = '.heading { font: serif x-large }' #: extra_css = None

#: If True empty feeds are removed from the output. #: This option has no effect if parse_index is overridden in #: the sub class. It is meant only for recipes that return a list #: of feeds using `feeds` or :meth:`get_feeds`. It is also used if you use #: the ignore_duplicate_articles option. remove_empty_feeds = False

#: List of regular expressions that determines which links to follow. #: If empty, it is ignored. Used only if is_link_wanted is #: not implemented. For example:: #: #: match_regexps = [r'page=[0-9]+'] #: #: will match all URLs that have `page=some number` in them. #: #: Only one of :attr:`BasicNewsRecipe.match_regexps` or #: :attr:`BasicNewsRecipe.filter_regexps` should be defined. match_regexps = []

#: List of regular expressions that determines which links to ignore. #: If empty it is ignored. Used only if is_link_wanted is not #: implemented. For example:: #: #: filter_regexps = [r'ads\.doubleclick\.net'] #: #: will remove all URLs that have `ads.doubleclick.net` in them. #: #: Only one of :attr:`BasicNewsRecipe.match_regexps` or #: :attr:`BasicNewsRecipe.filter_regexps` should be defined. filter_regexps = []

#: Recipe specific options to control the conversion of the downloaded #: content into an e-book. These will override any user or plugin specified #: values, so only use if absolutely necessary. For example:: #: #: conversion_options = { #: 'base_font_size' : 16, #: 'linearize_tables' : True, #: } #: conversion_options = {}

#: List of tags to be removed. Specified tags are removed from downloaded HTML. #: A tag is specified as a dictionary of the form:: #: #: { #: name : 'tag name', #e.g. 'div' #: attrs : a dictionary, #e.g. {'class': 'advertisment'} #: } #: #: All keys are optional. For a full explanation of the search criteria, see #: `Beautiful Soup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/#searching-the-tree>`__ #: A common example:: #: #: remove_tags = [dict(name='div', class_='advert')] #: #: This will remove all `<div class="advert">` tags and all #: their children from the downloaded :term:`HTML`. remove_tags = []

#: Remove all tags that occur after the specified tag. #: For the format for specifying a tag see :attr:`BasicNewsRecipe.remove_tags`. #: For example:: #: #: remove_tags_after = [dict(id='content')] #: #: will remove all #: tags after the first element with `id="content"`. remove_tags_after = None

#: Remove all tags that occur before the specified tag. #: For the format for specifying a tag see :attr:`BasicNewsRecipe.remove_tags`. #: For example:: #: #: remove_tags_before = dict(id='content') #: #: will remove all #: tags before the first element with `id="content"`. remove_tags_before = None

#: List of attributes to remove from all tags. #: For example:: #: #: remove_attributes = ['style', 'font'] remove_attributes = []

#: Keep only the specified tags and their children. #: For the format for specifying a tag see :attr:`BasicNewsRecipe.remove_tags`. #: If this list is not empty, then the `<body>` tag will be emptied and re-filled with #: the tags that match the entries in this list. For example:: #: #: keep_only_tags = [dict(id=['content', 'heading'])] #: #: will keep only tags that have an `id` attribute of `"content"` or `"heading"`. keep_only_tags = []

#: List of :term:`regexp` substitution rules to run on the downloaded :term:`HTML`. #: Each element of the #: list should be a two element tuple. The first element of the tuple should #: be a compiled regular expression and the second a callable that takes #: a single match object and returns a string to replace the match. For example:: #: #: preprocess_regexps = [ #: (re.compile(r'<!--Article ends here-->.*</body>', re.DOTALL|re.IGNORECASE), #: lambda match: '</body>'), #: ] #: #: will remove everything from `<!--Article ends here-->` to `</body>`. preprocess_regexps = []

#: The CSS that is used to style the templates, i.e., the navigation bars and #: the Tables of Contents. Rather than overriding this variable, you should #: use `extra_css` in your recipe to customize look and feel. template_css = ''' .article_date { color: gray; font-family: monospace; }

.article_description { text-indent: 0pt; }

a.article { font-weight: bold; text-align:left; }

a.feed { font-weight: bold; }

.calibre_navbar { font-family:monospace; } '''

#: By default, calibre will use a default image for the masthead (Kindle only). #: Override this in your recipe to provide a URL to use as a masthead. masthead_url = None

#: By default, the cover image returned by get_cover_url() will be used as #: the cover for the periodical. Overriding this in your recipe instructs #: calibre to render the downloaded cover into a frame whose width and height #: are expressed as a percentage of the downloaded cover. #: cover_margins = (10, 15, '#ffffff') pads the cover with a white margin #: 10px on the left and right, 15px on the top and bottom. #: Color names are defined `here <https://www.imagemagick.org/script/color.php>`_. #: Note that for some reason, white does not always work in Windows. Use #: #ffffff instead cover_margins = (0, 0, '#ffffff')

#: Set to a non empty string to disable this recipe. #: The string will be used as the disabled message recipe_disabled = None

#: Ignore duplicates of articles that are present in more than one section. #: A duplicate article is an article that has the same title and/or URL. #: To ignore articles with the same title, set this to:: #: #: ignore_duplicate_articles = {'title'} #: #: To use URLs instead, set it to:: #: #: ignore_duplicate_articles = {'url'} #: #: To match on title or URL, set it to:: #: #: ignore_duplicate_articles = {'title', 'url'} ignore_duplicate_articles = None

# The following parameters control how the recipe attempts to minimize # JPEG image sizes

#: Set this to False to ignore all scaling and compression parameters and #: pass images through unmodified. If True and the other compression #: parameters are left at their default values, JPEG images will be scaled to fit #: in the screen dimensions set by the output profile and compressed to size at #: most (w * h)/16 where w x h are the scaled image dimensions. compress_news_images = False

#: The factor used when auto compressing JPEG images. If set to None, #: auto compression is disabled. Otherwise, the images will be reduced in size to #: (w * h)/compress_news_images_auto_size bytes if possible by reducing #: the quality level, where w x h are the image dimensions in pixels. #: The minimum JPEG quality will be 5/100 so it is possible this constraint #: will not be met. This parameter can be overridden by the parameter #: compress_news_images_max_size which provides a fixed maximum size for images. #: Note that if you enable scale_news_images_to_device then the image will #: first be scaled and then its quality lowered until its size is less than #: (w * h)/factor where w and h are now the *scaled* image dimensions. In #: other words, this compression happens after scaling. compress_news_images_auto_size = 16

#: Set JPEG quality so images do not exceed the size given (in KBytes). #: If set, this parameter overrides auto compression via compress_news_images_auto_size. #: The minimum JPEG quality will be 5/100 so it is possible this constraint #: will not be met. compress_news_images_max_size = None

#: Rescale images to fit in the device screen dimensions set by the output profile. #: Ignored if no output profile is set. scale_news_images_to_device = True

#: Maximum dimensions (w,h) to scale images to. If scale_news_images_to_device is True #: this is set to the device screen dimensions set by the output profile unless #: there is no profile set, in which case it is left at whatever value it has been #: assigned (default None). scale_news_images = None

#: If set to True then links in downloaded articles that point to other downloaded articles are #: changed to point to the downloaded copy of the article rather than its original web URL. If you #: set this to True, you might also need to implement :meth:`canonicalize_internal_url` to work #: with the URL scheme of your particular website. resolve_internal_links = False

#: Set to False if you do not want to use gzipped transfers. Note that some old servers flake out with gzip handle_gzip = True

# See the built-in recipes for examples of these settings.

def short_title(self): return force_unicode(self.title, preferred_encoding)

[belgeler] def is_link_wanted(self, url, tag): ''' Return True if the link should be followed or False otherwise. By default, raises NotImplementedError which causes the downloader to ignore it.

:param url: The URL to be followed :param tag: The tag from which the URL was derived ''' raise NotImplementedError()

[belgeler] def get_extra_css(self): ''' By default returns `self.extra_css`. Override if you want to programmatically generate the extra_css. ''' return self.extra_css

[belgeler] def get_cover_url(self): ''' Return a :term:`URL` to the cover image for this issue or `None`. By default it returns the value of the member `self.cover_url` which is normally `None`. If you want your recipe to download a cover for the e-book override this method in your subclass, or set the member variable `self.cover_url` before this method is called. ''' return getattr(self, 'cover_url', None)

[belgeler] def get_masthead_url(self): ''' Return a :term:`URL` to the masthead image for this issue or `None`. By default it returns the value of the member `self.masthead_url` which is normally `None`. If you want your recipe to download a masthead for the e-book override this method in your subclass, or set the member variable `self.masthead_url` before this method is called. Masthead images are used in Kindle MOBI files. ''' return getattr(self, 'masthead_url', None)

[belgeler] def get_feeds(self): ''' Return a list of :term:`RSS` feeds to fetch for this profile. Each element of the list must be a 2-element tuple of the form (title, url). If title is None or an empty string, the title from the feed is used. This method is useful if your recipe needs to do some processing to figure out the list of feeds to download. If so, override in your subclass. ''' if not self.feeds: raise NotImplementedError() if self.test: return self.feeds[:self.test[0]] return self.feeds

[belgeler] def get_url_specific_delay(self, url): ''' Return the delay in seconds before downloading this URL. If you want to programmatically determine the delay for the specified URL, override this method in your subclass, returning self.delay by default for URLs you do not want to affect.

:return: A floating point number, the delay in seconds. ''' return self.delay

[belgeler] @classmethod def print_version(cls, url): ''' Take a `url` pointing to the webpage with article content and return the :term:`URL` pointing to the print version of the article. By default does nothing. For example::

def print_version(self, url): return url + '?&pagewanted=print'

''' raise NotImplementedError()

[belgeler] @classmethod def image_url_processor(cls, baseurl, url): ''' Perform some processing on image urls (perhaps removing size restrictions for dynamically generated images, etc.) and return the precessed URL. Return None or an empty string to skip fetching the image. ''' return url

[belgeler] def preprocess_image(self, img_data, image_url): ''' Perform some processing on downloaded image data. This is called on the raw data before any resizing is done. Must return the processed raw data. Return None to skip the image. ''' return img_data

[belgeler] def get_browser(self, *args, **kwargs): ''' Return a browser instance used to fetch documents from the web. By default it returns a `mechanize <https://mechanize.readthedocs.io/en/latest/>`_ browser instance that supports cookies, ignores robots.txt, handles refreshes and has a mozilla firefox user agent.

If your recipe requires that you login first, override this method in your subclass. For example, the following code is used in the New York Times recipe to login for full access::

def get_browser(self): br = BasicNewsRecipe.get_browser(self) if self.username is not None and self.password is not None: br.open('https://www.nytimes.com/auth/login') br.select_form(name='login') br['USERID'] = self.username br['PASSWORD'] = self.password br.submit() return br

''' if 'user_agent' not in kwargs: # More and more news sites are serving JPEG XR images to IE ua = getattr(self, 'last_used_user_agent', None) or self.calibre_most_common_ua or random_user_agent(allow_ie=False) kwargs['user_agent'] = self.last_used_user_agent = ua self.log('Using user agent:', kwargs['user_agent']) br = browser(*args, **kwargs) br.addheaders += [('Accept', '*/*')] if self.handle_gzip: br.set_handle_gzip(True) return br

[belgeler] def clone_browser(self, br): ''' Clone the browser br. Cloned browsers are used for multi-threaded downloads, since mechanize is not thread safe. The default cloning routines should capture most browser customization, but if you do something exotic in your recipe, you should override this method in your recipe and clone manually.

Cloned browser instances use the same, thread-safe CookieJar by default, unless you have customized cookie handling. ''' if callable(getattr(br, 'clone_browser', None)): return br.clone_browser()

# Uh-oh recipe using something exotic, call get_browser return self.get_browser()

@property def cloned_browser(self): if hasattr(self.get_browser, 'is_base_class_implementation'): # We are using the default get_browser, which means no need to # clone br = BasicNewsRecipe.get_browser(self) else: br = self.clone_browser(self.browser) return br

[belgeler] def get_article_url(self, article): ''' Override in a subclass to customize extraction of the :term:`URL` that points to the content for each article. Return the article URL. It is called with `article`, an object representing a parsed article from a feed. See `feedparser <https://pythonhosted.org/feedparser/>`_. By default it looks for the original link (for feeds syndicated via a service like FeedBurner or Pheedo) and if found, returns that or else returns `article.link <https://pythonhosted.org/feedparser/reference-entry-link.html>`_. ''' for key in article.keys(): if key.endswith('_origlink'): url = article[key] if url and (url.startswith('http://') or url.startswith('https://')): return url ans = article.get('link', None) if not ans and getattr(article, 'links', None): for item in article.links: if item.get('rel', 'alternate') == 'alternate': ans = item['href'] break return ans

[belgeler] def skip_ad_pages(self, soup): ''' This method is called with the source of each downloaded :term:`HTML` file, before any of the cleanup attributes like remove_tags, keep_only_tags are applied. Note that preprocess_regexps will have already been applied. It is meant to allow the recipe to skip ad pages. If the soup represents an ad page, return the HTML of the real page. Otherwise return None.

`soup`: A `BeautifulSoup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`__ instance containing the downloaded :term:`HTML`. ''' return None

[belgeler] def abort_article(self, msg=None): ''' Call this method inside any of the preprocess methods to abort the download for the current article. Useful to skip articles that contain inappropriate content, such as pure video articles. ''' raise AbortArticle(msg or _('Article download aborted'))

[belgeler] def preprocess_raw_html(self, raw_html, url): ''' This method is called with the source of each downloaded :term:`HTML` file, before it is parsed into an object tree. raw_html is a unicode string representing the raw HTML downloaded from the web. url is the URL from which the HTML was downloaded.

Note that this method acts *before* preprocess_regexps.

This method must return the processed raw_html as a unicode object. ''' return raw_html
def preprocess_raw_html_(self, raw_html, url): raw_html = self.preprocess_raw_html(raw_html, url) if self.auto_cleanup: try: raw_html = self.extract_readable_article(raw_html, url) except: self.log.exception('Auto cleanup of URL: %r failed'%url)

return raw_html

[belgeler] def preprocess_html(self, soup): ''' This method is called with the source of each downloaded :term:`HTML` file, before it is parsed for links and images. It is called after the cleanup as specified by remove_tags etc. It can be used to do arbitrarily powerful pre-processing on the :term:`HTML`. It should return `soup` after processing it.

`soup`: A `BeautifulSoup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`__ instance containing the downloaded :term:`HTML`. ''' return soup

[belgeler] def postprocess_html(self, soup, first_fetch): ''' This method is called with the source of each downloaded :term:`HTML` file, after it is parsed for links and images. It can be used to do arbitrarily powerful post-processing on the :term:`HTML`. It should return `soup` after processing it.

:param soup: A `BeautifulSoup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`__ instance containing the downloaded :term:`HTML`. :param first_fetch: True if this is the first page of an article.

''' return soup

[belgeler] def cleanup(self): ''' Called after all articles have been download. Use it to do any cleanup like logging out of subscription sites, etc. ''' pass

[belgeler] def canonicalize_internal_url(self, url, is_link=True): ''' Return a set of canonical representations of ``url``. The default implementation uses just the server hostname and path of the URL, ignoring any query parameters, fragments, etc. The canonical representations must be unique across all URLs for this news source. If they are not, then internal links may be resolved incorrectly.

:param is_link: Is True if the URL is coming from an internal link in an HTML file. False if the URL is the URL used to download an article. ''' try: parts = urlparse(url) except Exception: self.log.error('Failed to parse url: %r, ignoring' % url) return frozenset() nl = parts.netloc path = parts.path or '' if isinstance(nl, bytes): nl = nl.decode('utf-8', 'replace') if isinstance(path, bytes): path = path.decode('utf-8', 'replace') return frozenset({(nl, path.rstrip('/'))})

[belgeler] def index_to_soup(self, url_or_raw, raw=False, as_tree=False, save_raw=None): ''' Convenience method that takes an URL to the index page and returns a `BeautifulSoup <https://www.crummy.com/software/BeautifulSoup/bs4/doc>`__ of it.

`url_or_raw`: Either a URL or the downloaded index page as a string ''' if re.match((br'\w+://' if isinstance(url_or_raw, bytes) else r'\w+://'), url_or_raw): # We may be called in a thread (in the skip_ad_pages method), so # clone the browser to be safe. We cannot use self.cloned_browser # as it may or may not actually clone the browser, depending on if # the recipe implements get_browser() or not br = self.clone_browser(self.browser) open_func = getattr(br, 'open_novisit', br.open) with closing(open_func(url_or_raw, timeout=self.timeout)) as f: _raw = f.read() if not _raw: raise RuntimeError('Could not fetch index from %s'%url_or_raw) else: _raw = url_or_raw if raw: return _raw if not isinstance(_raw, str) and self.encoding: if callable(self.encoding): _raw = self.encoding(_raw) else: _raw = _raw.decode(self.encoding, 'replace') from calibre.ebooks.chardet import strip_encoding_declarations, xml_to_unicode from calibre.utils.cleantext import clean_xml_chars if isinstance(_raw, str): _raw = strip_encoding_declarations(_raw) else: _raw = xml_to_unicode(_raw, strip_encoding_pats=True, resolve_entities=True)[0] _raw = clean_xml_chars(_raw) if save_raw: with open(save_raw, 'wb') as f: f.write(_raw.encode('utf-8')) if as_tree: from html5_parser import parse return parse(_raw) return BeautifulSoup(_raw)[belgeler] def extract_readable_article(self, html, url): ''' Extracts main article content from 'html', cleans up and returns as a (article_html, extracted_title) tuple. Based on the original readability algorithm by Arc90. ''' from lxml.html import document_fromstring, fragment_fromstring, tostring from calibre.ebooks.readability import readability doc = readability.Document(html, self.log, url=url, keep_elements=self.auto_cleanup_keep) article_html = doc.summary() extracted_title = doc.title()

try: frag = fragment_fromstring(article_html) except: doc = document_fromstring(article_html) frag = doc.xpath('//body')[-1] if frag.tag == 'html': root = frag elif frag.tag == 'body': root = document_fromstring( '<html><head><title>%s</title></head></html>' % extracted_title) root.append(frag) else: root = document_fromstring( '<html><head><title>%s</title></head><body/></html>' % extracted_title) root.xpath('//body')[0].append(frag)

body = root.xpath('//body')[0] has_title = False for x in body.iterdescendants(): if x.text == extracted_title: has_title = True inline_titles = body.xpath('//h1|//h2') if not has_title and not inline_titles: heading = body.makeelement('h2') heading.text = extracted_title body.insert(0, heading)

raw_html = tostring(root, encoding='unicode')

return raw_html

[belgeler] def sort_index_by(self, index, weights): ''' Convenience method to sort the titles in `index` according to `weights`. `index` is sorted in place. Returns `index`.

`index`: A list of titles.

`weights`: A dictionary that maps weights to titles. If any titles in index are not in weights, they are assumed to have a weight of 0. ''' weights = defaultdict(int, weights) index.sort(key=lambda x: weights[x]) return index

[belgeler] def parse_index(self): ''' This method should be implemented in recipes that parse a website instead of feeds to generate a list of articles. Typical uses are for news sources that have a "Print Edition" webpage that lists all the articles in the current print edition. If this function is implemented, it will be used in preference to :meth:`BasicNewsRecipe.parse_feeds`.

It must return a list. Each element of the list must be a 2-element tuple of the form ``('feed title', list of articles)``.

Each list of articles must contain dictionaries of the form::

{ 'title' : article title, 'url' : URL of print version, 'date' : The publication date of the article as a string, 'description' : A summary of the article 'content' : The full article (can be an empty string). Obsolete do not use, instead save the content to a temporary file and pass a file:///path/to/temp/file.html as the URL. }

For an example, see the recipe for downloading `The Atlantic`. In addition, you can add 'author' for the author of the article.

If you want to abort processing for some reason and have calibre show the user a simple message instead of an error, call :meth:`abort_recipe_processing`. ''' raise NotImplementedError()

[belgeler] def abort_recipe_processing(self, msg): ''' Causes the recipe download system to abort the download of this recipe, displaying a simple feedback message to the user. ''' from calibre.ebooks.conversion import ConversionUserFeedBack raise ConversionUserFeedBack(_('Failed to download %s')%self.title, msg)

[belgeler] def get_obfuscated_article(self, url): ''' If you set `articles_are_obfuscated` this method is called with every article URL. It should return the path to a file on the filesystem that contains the article HTML. That file is processed by the recursive HTML fetching engine, so it can contain links to pages/images on the web. Alternately, you can return a dictionary of the form: {'data': <HTML data>, 'url': <the resolved URL of the article>}. This avoids needing to create temporary files. The `url` key in the dictionary is useful if the effective URL of the article is different from the URL passed into this method, for example, because of redirects. It can be omitted if the URL is unchanged.

This method is typically useful for sites that try to make it difficult to access article content automatically. ''' raise NotImplementedError()

[belgeler] def add_toc_thumbnail(self, article, src): ''' Call this from populate_article_metadata with the src attribute of an <img> tag from the article that is appropriate for use as the thumbnail representing the article in the Table of Contents. Whether the thumbnail is actually used is device dependent (currently only used by the Kindles). Note that the referenced image must be one that was successfully downloaded, otherwise it will be ignored. ''' if not src or not hasattr(article, 'toc_thumbnail'): return src = src.replace('\\', '/') if re.search(r'feed_\d+/article_\d+/images/img', src, flags=re.I) is None: self.log.warn('Ignoring invalid TOC thumbnail image: %r'%src) return article.toc_thumbnail = re.sub(r'^.*?feed', 'feed', src, flags=re.IGNORECASE)

[belgeler] def populate_article_metadata(self, article, soup, first): ''' Called when each HTML page belonging to article is downloaded. Intended to be used to get article metadata like author/summary/etc. from the parsed HTML (soup).

:param article: A object of class :class:`calibre.web.feeds.Article`. If you change the summary, remember to also change the text_summary :param soup: Parsed HTML belonging to this article :param first: True iff the parsed HTML is the first page of the article. ''' pass

[belgeler] def postprocess_book(self, oeb, opts, log): ''' Run any needed post processing on the parsed downloaded e-book.

:param oeb: An OEBBook object :param opts: Conversion options ''' pass
def __init__(self, options, log, progress_reporter): ''' Initialize the recipe. :param options: Parsed commandline options :param log: Logging object :param progress_reporter: A Callable that takes two arguments: progress (a number between 0 and 1) and a string message. The message should be optional. ''' self.log = ThreadSafeWrapper(log) if not isinstance(self.title, str): self.title = str(self.title, 'utf-8', 'replace')

self.debug = options.verbose > 1 self.output_dir = os.path.abspath(os.getcwd()) self.verbose = options.verbose self.test = options.test if self.test and not isinstance(self.test, tuple): self.test = (2, 2) self.username = options.username self.password = options.password self.lrf = options.lrf self.output_profile = options.output_profile self.touchscreen = getattr(self.output_profile, 'touchscreen', False) if self.touchscreen: self.template_css += self.output_profile.touchscreen_news_css if self.test: self.max_articles_per_feed = self.test[1] self.simultaneous_downloads = min(4, self.simultaneous_downloads)

if self.debug: self.verbose = True self.report_progress = progress_reporter if self.needs_subscription and ( self.username is None or self.password is None or ( not self.username and not self.password)): if self.needs_subscription != 'optional': raise ValueError(_('The "%s" recipe needs a username and password.')%self.title)

self.browser = self.get_browser() self.image_map, self.image_counter = {}, 1 self.css_map = {}

web2disk_cmdline = ['web2disk', '--timeout', str(self.timeout), '--max-recursions', str(self.recursions), '--delay', str(self.delay), ]

if self.verbose: web2disk_cmdline.append('--verbose')

if self.no_stylesheets: web2disk_cmdline.append('--dont-download-stylesheets')

for reg in self.match_regexps: web2disk_cmdline.extend(['--match-regexp', reg])

for reg in self.filter_regexps: web2disk_cmdline.extend(['--filter-regexp', reg])

if options.output_profile.short_name in ('default', 'tablet'): self.scale_news_images_to_device = False elif self.scale_news_images_to_device: self.scale_news_images = options.output_profile.screen_size self.web2disk_options = web2disk_option_parser().parse_args(web2disk_cmdline)[0] for extra in ('keep_only_tags', 'remove_tags', 'preprocess_regexps', 'skip_ad_pages', 'preprocess_html', 'remove_tags_after', 'remove_tags_before', 'is_link_wanted', 'compress_news_images', 'compress_news_images_max_size', 'compress_news_images_auto_size', 'scale_news_images'): setattr(self.web2disk_options, extra, getattr(self, extra))

self.web2disk_options.postprocess_html = self._postprocess_html self.web2disk_options.preprocess_image = self.preprocess_image self.web2disk_options.encoding = self.encoding self.web2disk_options.preprocess_raw_html = self.preprocess_raw_html_ self.web2disk_options.get_delay = self.get_url_specific_delay if self.delay > 0: self.simultaneous_downloads = 1 self.navbar = templates.TouchscreenNavBarTemplate() if self.touchscreen else \ templates.NavBarTemplate() self.failed_downloads = [] self.partial_failures = [] self.aborted_articles = []

def _postprocess_html(self, soup, first_fetch, job_info): if self.no_stylesheets: for link in soup.findAll('link'): if (link.get('type') or 'text/css').lower() == 'text/css' and 'stylesheet' in (link.get('rel') or ('stylesheet',)): link.extract() for style in soup.findAll('style'): style.extract() head = soup.find('head') if not head: head = soup.find('body') if not head: head = soup.find(True) css = self.template_css + '\n\n' + (self.get_extra_css() or '') style = soup.new_tag('style', type='text/css', title='override_css') style.append(css) head.append(style) if first_fetch and job_info: url, f, a, feed_len = job_info body = soup.find('body') if body is not None: templ = self.navbar.generate(False, f, a, feed_len, not self.has_single_feed, url, __appname__, center=self.center_navbar, extra_css=self.get_extra_css() or '') elem = BeautifulSoup(templ.render(doctype='xhtml').decode('utf-8')).find('div') body.insert(0, elem) # This is needed because otherwise inserting elements into # the soup breaks find() soup = BeautifulSoup(soup.decode_contents()) if self.remove_javascript: for script in list(soup.findAll('script')): script.extract() for o in soup.findAll(onload=True): del o['onload']

for attr in self.remove_attributes: for x in soup.findAll(attrs={attr:True}): del x[attr] for bad_tag in list(soup.findAll(['base', 'iframe', 'canvas', 'embed', 'button', 'command', 'datalist', 'video', 'audio', 'noscript', 'link', 'meta'])): # link tags can be used for preloading causing network activity in # calibre viewer. meta tags can do all sorts of crazy things, # including http-equiv refresh, viewport shenanigans, etc. bad_tag.extract() # srcset causes some viewers, like calibre's to load images from the # web, and it also possible causes iBooks on iOS to barf, see # https://bugs.launchpad.net/bugs/1713986 for img in soup.findAll('img', srcset=True): del img['srcset']

ans = self.postprocess_html(soup, first_fetch)

# Nuke HTML5 tags for x in ans.findAll(['article', 'aside', 'header', 'footer', 'nav', 'figcaption', 'figure', 'section']): x.name = 'div'

if job_info: url, f, a, feed_len = job_info try: article = self.feed_objects[f].articles[a] except: self.log.exception('Failed to get article object for postprocessing') pass else: self.populate_article_metadata(article, ans, first_fetch) return ans

[belgeler] def download(self): ''' Download and pre-process all articles from the feeds in this recipe. This method should be called only once on a particular Recipe instance. Calling it more than once will lead to undefined behavior. :return: Path to index.html ''' try: res = self.build_index() self.report_progress(1, _('Download finished')) if self.failed_downloads: self.log.warning(_('Failed to download the following articles:')) for feed, article, debug in self.failed_downloads: self.log.warning(article.title, 'from', feed.title) self.log.debug(article.url) self.log.debug(debug) if self.partial_failures: self.log.warning(_('Failed to download parts of the following articles:')) for feed, atitle, aurl, debug in self.partial_failures: self.log.warning(atitle + _(' from ') + feed) self.log.debug(aurl) self.log.warning(_('\tFailed links:')) for l, tb in debug: self.log.warning(l) self.log.debug(tb) return res finally: self.cleanup()

@property def lang_for_html(self): try: lang = self.language.replace('_', '-').partition('-')[0].lower() if lang == 'und': lang = None except: lang = None return lang def feeds2index(self, feeds): templ = (templates.TouchscreenIndexTemplate if self.touchscreen else templates.IndexTemplate) templ = templ(lang=self.lang_for_html) css = self.template_css + '\n\n' +(self.get_extra_css() or '') timefmt = self.timefmt return templ.generate(self.title, "mastheadImage.jpg", timefmt, feeds, extra_css=css).render(doctype='xhtml')

@classmethod def description_limiter(cls, src): if not src: return '' src = force_unicode(src, 'utf-8') pos = cls.summary_length fuzz = 50 si = src.find(';', pos) if si > 0 and si-pos > fuzz: si = -1 gi = src.find('>', pos) if gi > 0 and gi-pos > fuzz: gi = -1 npos = max(si, gi) if npos < 0: npos = pos ans = src[:npos+1] if len(ans) < len(src): from calibre.utils.cleantext import clean_xml_chars

# Truncating the string could cause a dangling UTF-16 half-surrogate, which will cause lxml to barf, clean it ans = clean_xml_chars(ans) + '\u2026' return ans def feed2index(self, f, feeds): feed = feeds[f] if feed.image_url is not None: # Download feed image imgdir = os.path.join(self.output_dir, 'images') if not os.path.isdir(imgdir): os.makedirs(imgdir)

if feed.image_url in self.image_map: feed.image_url = self.image_map[feed.image_url] else: bn = urlsplit(feed.image_url).path if bn: bn = bn.rpartition('/')[-1] if bn: img = os.path.join(imgdir, 'feed_image_%d%s'%(self.image_counter, os.path.splitext(bn)[-1])) try: with open(img, 'wb') as fi, closing(self.browser.open(feed.image_url, timeout=self.timeout)) as r: fi.write(r.read()) self.image_counter += 1 feed.image_url = img self.image_map[feed.image_url] = img except: pass if isinstance(feed.image_url, bytes): feed.image_url = feed.image_url.decode(sys.getfilesystemencoding(), 'strict')

templ = (templates.TouchscreenFeedTemplate if self.touchscreen else templates.FeedTemplate) templ = templ(lang=self.lang_for_html) css = self.template_css + '\n\n' +(self.get_extra_css() or '')

return templ.generate(f, feeds, self.description_limiter, extra_css=css).render(doctype='xhtml')

def _fetch_article(self, url, dir_, f, a, num_of_feeds, preloaded=None): br = self.browser if hasattr(self.get_browser, 'is_base_class_implementation'): # We are using the default get_browser, which means no need to # clone br = BasicNewsRecipe.get_browser(self) else: br = self.clone_browser(self.browser) self.web2disk_options.browser = br fetcher = RecursiveFetcher(self.web2disk_options, self.log, self.image_map, self.css_map, (url, f, a, num_of_feeds)) fetcher.browser = br fetcher.base_dir = dir_ fetcher.current_dir = dir_ fetcher.show_progress = False fetcher.image_url_processor = self.image_url_processor if preloaded is not None: fetcher.preloaded_urls[url] = preloaded res, path, failures = fetcher.start_fetch(url), fetcher.downloaded_paths, fetcher.failed_links if not res or not os.path.exists(res): msg = _('Could not fetch article.') + ' ' if self.debug: msg += _('The debug traceback is available earlier in this log') else: msg += _('Run with -vv to see the reason') raise Exception(msg)

return res, path, failures def fetch_article(self, url, dir, f, a, num_of_feeds): return self._fetch_article(url, dir, f, a, num_of_feeds)

def fetch_obfuscated_article(self, url, dir, f, a, num_of_feeds): x = self.get_obfuscated_article(url) if isinstance(x, dict): data = x['data'] if isinstance(data, str): data = data.encode(self.encoding or 'utf-8') url = x.get('url', url) else: with open(x, 'rb') as of: data = of.read() os.remove(x) return self._fetch_article(url, dir, f, a, num_of_feeds, preloaded=data)

def fetch_embedded_article(self, article, dir, f, a, num_of_feeds): templ = templates.EmbeddedContent() raw = templ.generate(article).render('html') with PersistentTemporaryFile('_feeds2disk.html') as pt: pt.write(raw) url = ('file:'+pt.name) if iswindows else ('file://'+pt.name) return self._fetch_article(url, dir, f, a, num_of_feeds)

def remove_duplicate_articles(self, feeds): seen_keys = defaultdict(set) remove = [] for f in feeds: for article in f: for key in self.ignore_duplicate_articles: val = getattr(article, key) seen = seen_keys[key] if val: if val in seen: remove.append((f, article)) else: seen.add(val)

for feed, article in remove: self.log.debug('Removing duplicate article: %s from section: %s'%( article.title, feed.title)) feed.remove_article(article)

if self.remove_empty_feeds: feeds = [f for f in feeds if len(f) > 0] return feeds def build_index(self): self.report_progress(0, _('Fetching feeds...')) feeds = None try: feeds = feeds_from_index(self.parse_index(), oldest_article=self.oldest_article, max_articles_per_feed=self.max_articles_per_feed, log=self.log) self.report_progress(0, _('Got feeds from index page')) except NotImplementedError: pass if feeds is None: feeds = self.parse_feeds()

if not feeds: raise ValueError('No articles found, aborting')

if self.ignore_duplicate_articles is not None: feeds = self.remove_duplicate_articles(feeds)

self.report_progress(0, _('Trying to download cover...')) self.download_cover() self.report_progress(0, _('Generating masthead...')) self.resolve_masthead()

if self.test: feeds = feeds[:self.test[0]] self.has_single_feed = len(feeds) == 1 index = os.path.join(self.output_dir, 'index.html')

html = self.feeds2index(feeds) with open(index, 'wb') as fi: fi.write(html)

self.jobs = []

if self.reverse_article_order: for feed in feeds: if hasattr(feed, 'reverse'): feed.reverse()

self.feed_objects = feeds for f, feed in enumerate(feeds): feed_dir = os.path.join(self.output_dir, 'feed_%d'%f) if not os.path.isdir(feed_dir): os.makedirs(feed_dir)

for a, article in enumerate(feed): if a >= self.max_articles_per_feed: break art_dir = os.path.join(feed_dir, 'article_%d'%a) if not os.path.isdir(art_dir): os.makedirs(art_dir) try: url = self.print_version(article.url) except NotImplementedError: url = article.url except: self.log.exception('Failed to find print version for: '+article.url) url = None if not url: continue func, arg = (self.fetch_embedded_article, article) \ if self.use_embedded_content or (self.use_embedded_content is None and feed.has_embedded_content()) \ else \ ((self.fetch_obfuscated_article if self.articles_are_obfuscated else self.fetch_article), url) req = WorkRequest(func, (arg, art_dir, f, a, len(feed)), {}, (f, a), self.article_downloaded, self.error_in_article_download) req.feed = feed req.article = article req.feed_dir = feed_dir self.jobs.append(req)

self.jobs_done = 0 tp = ThreadPool(self.simultaneous_downloads) for req in self.jobs: tp.putRequest(req, block=True, timeout=0)

self.report_progress(0, ngettext( 'Starting download in a single thread...', 'Starting download [{} threads]...', self.simultaneous_downloads).format(self.simultaneous_downloads)) while True: try: tp.poll() time.sleep(0.1) except NoResultsPending: break for f, feed in enumerate(feeds): html = self.feed2index(f,feeds) feed_dir = os.path.join(self.output_dir, 'feed_%d'%f) with open(os.path.join(feed_dir, 'index.html'), 'wb') as fi: fi.write(html) self.create_opf(feeds) self.report_progress(1, _('Feeds downloaded to %s')%index)

return index def _download_cover(self): self.cover_path = None try: cu = self.get_cover_url() except Exception as err: self.log.error(_('Could not download cover: %s')%as_unicode(err)) self.log.debug(traceback.format_exc()) else: if not cu: return cdata = None if hasattr(cu, 'read'): cdata = cu.read() cu = getattr(cu, 'name', 'cover.jpg') elif os.access(cu, os.R_OK): with open(cu, 'rb') as f: cdata = f.read() else: self.report_progress(1, _('Downloading cover from %s')%cu) with closing(self.browser.open(cu, timeout=self.timeout)) as r: cdata = r.read() if not cdata: return ext = cu.split('/')[-1].rpartition('.')[-1].lower().strip() if ext == 'pdf': from calibre.ebooks.metadata.pdf import get_metadata stream = io.BytesIO(cdata) cdata = None mi = get_metadata(stream) if mi.cover_data and mi.cover_data[1]: cdata = mi.cover_data[1] if not cdata: return if self.cover_margins[0] or self.cover_margins[1]: cdata = image_to_data(add_borders_to_image(cdata, left=self.cover_margins[0],right=self.cover_margins[0], top=self.cover_margins[1],bottom=self.cover_margins[1], border_color=self.cover_margins[2]))

cpath = os.path.join(self.output_dir, 'cover.jpg') save_cover_data_to(cdata, cpath) self.cover_path = cpath def download_cover(self): self.cover_path = None try: self._download_cover() except: self.log.exception('Failed to download cover') self.cover_path = None def _download_masthead(self, mu): if hasattr(mu, 'rpartition'): ext = mu.rpartition('.')[-1] if '?' in ext: ext = '' else: ext = mu.name.rpartition('.')[-1] ext = ext.lower() if ext else 'jpg' mpath = os.path.join(self.output_dir, 'masthead_source.'+ext) outfile = os.path.join(self.output_dir, 'mastheadImage.jpg') if hasattr(mu, 'read'): with open(mpath, 'wb') as mfile: mfile.write(mu.read()) elif os.access(mu, os.R_OK): with open(mpath, 'wb') as mfile: mfile.write(open(mu, 'rb').read()) else: with open(mpath, 'wb') as mfile, closing(self.browser.open(mu, timeout=self.timeout)) as r: mfile.write(r.read()) self.report_progress(1, _('Masthead image downloaded')) self.prepare_masthead_image(mpath, outfile) self.masthead_path = outfile if os.path.exists(mpath): os.remove(mpath)

def download_masthead(self, url): try: self._download_masthead(url) except: self.log.exception("Failed to download supplied masthead_url")

def resolve_masthead(self): self.masthead_path = None try: murl = self.get_masthead_url() except: self.log.exception('Failed to get masthead url') murl = None if murl is not None: # Try downloading the user-supplied masthead_url # Failure sets self.masthead_path to None self.download_masthead(murl) if self.masthead_path is None: self.log.info("Synthesizing mastheadImage") self.masthead_path = os.path.join(self.output_dir, 'mastheadImage.jpg') try: self.default_masthead_image(self.masthead_path) except: self.log.exception('Failed to generate default masthead image') self.masthead_path = None

[belgeler] def default_cover(self, cover_file): ''' Create a generic cover for recipes that don't have a cover ''' try: from calibre.ebooks.covers import create_cover title = self.title if isinstance(self.title, str) else \ self.title.decode(preferred_encoding, 'replace') date = strftime(self.timefmt).replace('[', '').replace(']', '') img_data = create_cover(title, [date]) cover_file.write(img_data) cover_file.flush() except: self.log.exception('Failed to generate default cover') return False return True

[belgeler] def get_masthead_title(self): 'Override in subclass to use something other than the recipe title' return self.title

MI_WIDTH = 600 MI_HEIGHT = 60 def default_masthead_image(self, out_path): from calibre.ebooks import generate_masthead generate_masthead(self.get_masthead_title(), output_path=out_path, width=self.MI_WIDTH, height=self.MI_HEIGHT)

def prepare_masthead_image(self, path_to_image, out_path): prepare_masthead_image(path_to_image, out_path, self.MI_WIDTH, self.MI_HEIGHT)

[belgeler] def publication_date(self): ''' Use this method to set the date when this issue was published. Defaults to the moment of download. Must return a :class:`datetime.datetime` object. ''' return nowf()

def create_opf(self, feeds, dir=None): if dir is None: dir = self.output_dir title = self.short_title() pdate = self.publication_date() if self.output_profile.periodical_date_in_title: title += strftime(self.timefmt, pdate) mi = MetaInformation(title, [__appname__]) mi.publisher = __appname__ mi.author_sort = __appname__ if self.publication_type: mi.publication_type = 'periodical:'+self.publication_type+':'+self.short_title() mi.timestamp = nowf() article_titles, aseen = [], set() for (af, aa) in self.aborted_articles: aseen.add(aa.title) for (ff, fa, tb) in self.failed_downloads: aseen.add(fa.title) for f in feeds: for a in f: if a.title and a.title not in aseen: aseen.add(a.title) article_titles.append(force_unicode(a.title, 'utf-8'))

desc = self.description if not isinstance(desc, str): desc = desc.decode('utf-8', 'replace') mi.comments = (_('Articles in this issue:' ) + '\n\n' + '\n\n'.join(article_titles)) + '\n\n' + desc language = canonicalize_lang(self.language) if language is not None: mi.language = language mi.pubdate = pdate opf_path = os.path.join(dir, 'index.opf') ncx_path = os.path.join(dir, 'index.ncx')

opf = OPFCreator(dir, mi) # Add mastheadImage entry to <guide> section mp = getattr(self, 'masthead_path', None) if mp is not None and os.access(mp, os.R_OK): from calibre.ebooks.metadata.opf2 import Guide ref = Guide.Reference(os.path.basename(self.masthead_path), os.getcwd()) ref.type = 'masthead' ref.title = 'Masthead Image' opf.guide.append(ref)

manifest = [os.path.join(dir, 'feed_%d'%i) for i in range(len(feeds))] manifest.append(os.path.join(dir, 'index.html')) manifest.append(os.path.join(dir, 'index.ncx'))

# Get cover cpath = getattr(self, 'cover_path', None) if cpath is None: pf = open(os.path.join(dir, 'cover.jpg'), 'wb') if self.default_cover(pf): cpath = pf.name if cpath is not None and os.access(cpath, os.R_OK): opf.cover = cpath manifest.append(cpath)

# Get masthead mpath = getattr(self, 'masthead_path', None) if mpath is not None and os.access(mpath, os.R_OK): manifest.append(mpath)

opf.create_manifest_from_files_in(manifest) for mani in opf.manifest: if mani.path.endswith('.ncx'): mani.id = 'ncx' if mani.path.endswith('mastheadImage.jpg'): mani.id = 'masthead-image'

entries = ['index.html'] toc = TOC(base_path=dir) self.play_order_counter = 0 self.play_order_map = {}

self.article_url_map = aumap = defaultdict(set)

def feed_index(num, parent): f = feeds[num] for j, a in enumerate(f): if getattr(a, 'downloaded', False): adir = 'feed_%d/article_%d/'%(num, j) auth = a.author if not auth: auth = None desc = a.text_summary if not desc: desc = None else: desc = self.description_limiter(desc) tt = a.toc_thumbnail if a.toc_thumbnail else None entries.append('%sindex.html'%adir) po = self.play_order_map.get(entries[-1], None) if po is None: self.play_order_counter += 1 po = self.play_order_counter arelpath = '%sindex.html'%adir for curl in self.canonicalize_internal_url(a.orig_url, is_link=False): aumap[curl].add(arelpath) article_toc_entry = parent.add_item(arelpath, None, a.title if a.title else _('Untitled article'), play_order=po, author=auth, description=desc, toc_thumbnail=tt) for entry in a.internal_toc_entries: anchor = entry.get('anchor') if anchor: self.play_order_counter += 1 po += 1 article_toc_entry.add_item( arelpath, entry['anchor'], entry['title'] or _('Unknown section'), play_order=po ) last = os.path.join(self.output_dir, ('%sindex.html'%adir).replace('/', os.sep)) for sp in a.sub_pages: prefix = os.path.commonprefix([opf_path, sp]) relp = sp[len(prefix):] entries.append(relp.replace(os.sep, '/')) last = sp if os.path.exists(last): with open(last, 'rb') as fi: src = fi.read().decode('utf-8') soup = BeautifulSoup(src) body = soup.find('body') if body is not None: prefix = '/'.join('..'for i in range(2*len(re.findall(r'link\d+', last)))) templ = self.navbar.generate(True, num, j, len(f), not self.has_single_feed, a.orig_url, __appname__, prefix=prefix, center=self.center_navbar) elem = BeautifulSoup(templ.render(doctype='xhtml').decode('utf-8')).find('div') body.insert(len(body.contents), elem) with open(last, 'wb') as fi: fi.write(str(soup).encode('utf-8')) if len(feeds) == 0: raise Exception('All feeds are empty, aborting.')

if len(feeds) > 1: for i, f in enumerate(feeds): entries.append('feed_%d/index.html'%i) po = self.play_order_map.get(entries[-1], None) if po is None: self.play_order_counter += 1 po = self.play_order_counter auth = getattr(f, 'author', None) if not auth: auth = None desc = getattr(f, 'description', None) if not desc: desc = None feed_index(i, toc.add_item('feed_%d/index.html'%i, None, f.title, play_order=po, description=desc, author=auth))

else: entries.append('feed_%d/index.html'%0) feed_index(0, toc)

for i, p in enumerate(entries): entries[i] = os.path.join(dir, p.replace('/', os.sep)) opf.create_spine(entries) opf.set_toc(toc)

with open(opf_path, 'wb') as opf_file, open(ncx_path, 'wb') as ncx_file: opf.render(opf_file, ncx_file)

def article_downloaded(self, request, result): index = os.path.join(os.path.dirname(result[0]), 'index.html') if index != result[0]: if os.path.exists(index): os.remove(index) os.rename(result[0], index) a = request.requestID[1]

article = request.article self.log.debug('Downloaded article:', article.title, 'from', article.url) article.orig_url = article.url article.url = 'article_%d/index.html'%a article.downloaded = True article.sub_pages = result[1][1:] self.jobs_done += 1 self.report_progress(float(self.jobs_done)/len(self.jobs), _('Article downloaded: %s')%force_unicode(article.title)) if result[2]: self.partial_failures.append((request.feed.title, article.title, article.url, result[2]))

def error_in_article_download(self, request, traceback): self.jobs_done += 1 if traceback and re.search('^AbortArticle:', traceback, flags=re.M) is not None: self.log.warn('Aborted download of article:', request.article.title, 'from', request.article.url) self.report_progress(float(self.jobs_done)/len(self.jobs), _('Article download aborted: %s')%force_unicode(request.article.title)) self.aborted_articles.append((request.feed, request.article)) else: self.log.error('Failed to download article:', request.article.title, 'from', request.article.url) self.log.debug(traceback) self.log.debug('\n') self.report_progress(float(self.jobs_done)/len(self.jobs), _('Article download failed: %s')%force_unicode(request.article.title)) self.failed_downloads.append((request.feed, request.article, traceback))

[belgeler] def parse_feeds(self): ''' Create a list of articles from the list of feeds returned by :meth:`BasicNewsRecipe.get_feeds`. Return a list of :class:`Feed` objects. ''' feeds = self.get_feeds() parsed_feeds = [] br = self.browser for obj in feeds: if isinstance(obj, string_or_bytes): title, url = None, obj else: title, url = obj if isinstance(title, bytes): title = title.decode('utf-8') if isinstance(url, bytes): url = url.decode('utf-8') if url.startswith('feed://'): url = 'http'+url[4:] self.report_progress(0, _('Fetching feed')+' %s...'%(title if title else url)) try: purl = urlparse(url, allow_fragments=False) if purl.username or purl.password: hostname = purl.hostname if purl.port: hostname += f':{purl.port}' url = purl._replace(netloc=hostname).geturl() if purl.username and purl.password: br.add_password(url, purl.username, purl.password) with closing(br.open_novisit(url, timeout=self.timeout)) as f: raw = f.read() parsed_feeds.append(feed_from_xml( raw, title=title, log=self.log, oldest_article=self.oldest_article, max_articles_per_feed=self.max_articles_per_feed, get_article_url=self.get_article_url )) except Exception as err: feed = Feed() msg = 'Failed feed: %s'%(title if title else url) feed.populate_from_preparsed_feed(msg, []) feed.description = as_unicode(err) parsed_feeds.append(feed) self.log.exception(msg) delay = self.get_url_specific_delay(url) if delay > 0: time.sleep(delay)

remove = [fl for fl in parsed_feeds if len(fl) == 0 and self.remove_empty_feeds] for f in remove: parsed_feeds.remove(f)

return parsed_feeds[belgeler] @classmethod def tag_to_string(self, tag, use_alt=True, normalize_whitespace=True): ''' Convenience method to take a `BeautifulSoup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`_ :code:`Tag` and extract the text from it recursively, including any CDATA sections and alt tag attributes. Return a possibly empty Unicode string.

`use_alt`: If `True` try to use the alt attribute for tags that don't have any textual content

`tag`: `BeautifulSoup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`_ :code:`Tag` ''' if tag is None: return '' if isinstance(tag, string_or_bytes): return tag if callable(getattr(tag, 'xpath', None)) and not hasattr(tag, 'contents'): # a lxml tag from lxml.etree import tostring ans = tostring(tag, method='text', encoding='unicode', with_tail=False) else: strings = [] for item in tag.contents: if isinstance(item, (NavigableString, CData)): strings.append(item.string) elif isinstance(item, Tag): res = self.tag_to_string(item) if res: strings.append(res) elif use_alt: try: strings.append(item['alt']) except KeyError: pass ans = ''.join(strings) if normalize_whitespace: ans = re.sub(r'\s+', ' ', ans) return ans@classmethod def soup(cls, raw): return BeautifulSoup(raw)

[belgeler] @classmethod def adeify_images(cls, soup): ''' If your recipe when converted to EPUB has problems with images when viewed in Adobe Digital Editions, call this method from within :meth:`postprocess_html`. ''' for item in soup.findAll('img'): for attrib in ['height','width','border','align','style']: try: del item[attrib] except KeyError: pass oldParent = item.parent myIndex = oldParent.contents.index(item) item.extract() divtag = soup.new_tag('div') brtag = soup.new_tag('br') oldParent.insert(myIndex,divtag) divtag.append(item) divtag.append(brtag) return soup
def internal_postprocess_book(self, oeb, opts, log): if self.resolve_internal_links and self.article_url_map: seen = set() for item in oeb.spine: for a in item.data.xpath('//*[local-name()="a" and @href]'): if a.get('rel') == 'calibre-downloaded-from': continue url = a.get('href') for curl in self.canonicalize_internal_url(url): articles = self.article_url_map.get(curl) if articles: arelpath = sorted(articles, key=numeric_sort_key)[0] a.set('href', item.relhref(arelpath)) if url not in seen: log.debug(f'Resolved internal URL: {url} -> {arelpath}') seen.add(url)class CustomIndexRecipe(BasicNewsRecipe):

def custom_index(self): ''' Return the filesystem path to a custom HTML document that will serve as the index for this recipe. The index document will typically contain many `<a href="...">` tags that point to resources on the internet that should be downloaded. ''' raise NotImplementedError def create_opf(self): mi = MetaInformation(self.title + strftime(self.timefmt), [__appname__]) mi.publisher = __appname__ mi.author_sort = __appname__ mi = OPFCreator(self.output_dir, mi) mi.create_manifest_from_files_in([self.output_dir]) mi.create_spine([os.path.join(self.output_dir, 'index.html')]) with open(os.path.join(self.output_dir, 'index.opf'), 'wb') as opf_file: mi.render(opf_file)

def download(self): index = os.path.abspath(self.custom_index()) url = 'file:'+index if iswindows else 'file://'+index self.web2disk_options.browser = self.clone_browser(self.browser) fetcher = RecursiveFetcher(self.web2disk_options, self.log) fetcher.base_dir = self.output_dir fetcher.current_dir = self.output_dir fetcher.show_progress = False res = fetcher.start_fetch(url) self.create_opf() return res class AutomaticNewsRecipe(BasicNewsRecipe):

auto_cleanup = True class CalibrePeriodical(BasicNewsRecipe):

#: Set this to the slug for the calibre periodical calibre_periodicals_slug = None

LOG_IN = 'https://news.calibre-ebook.com/accounts/login' needs_subscription = True __author__ = 'calibre Periodicals'

def get_browser(self): br = BasicNewsRecipe.get_browser(self) br.open(self.LOG_IN) br.select_form(name='login') br['username'] = self.username br['password'] = self.password raw = br.submit().read() if 'href="/my-account"' not in raw: raise LoginFailed( _('Failed to log in, check your username and password for' ' the calibre Periodicals service.'))

return br get_browser.is_base_class_implementation = True def download(self): self.log('Fetching downloaded recipe') try: raw = self.browser.open_novisit( 'https://news.calibre-ebook.com/subscribed_files/%s/0/temp.downloaded_recipe' % self.calibre_periodicals_slug ).read() except Exception as e: if hasattr(e, 'getcode') and e.getcode() == 403: raise DownloadDenied( _('You do not have permission to download this issue.' ' Either your subscription has expired or you have' ' exceeded the maximum allowed downloads for today.')) raise f = io.BytesIO(raw) from calibre.utils.zipfile import ZipFile zf = ZipFile(f) zf.extractall() zf.close() from glob import glob from calibre.web.feeds.recipes import compile_recipe try: recipe = compile_recipe(open(glob('*.recipe')[0], 'rb').read()) self.conversion_options = recipe.conversion_options except: self.log.exception('Failed to compile downloaded recipe') return os.path.abspath('index.html')

© Telif hakkı Kovid Goyal. En son May 03, 2024 tarihinde güncellendi.

Table of Contents
calibre Kullanım Kılavuzu

Grafik Kullanıcı Arayüzü

1. Eylemler

1. Kitap ekle
2. Nitelik düzenleme
3. Kitapları dönüştür
4. Görünüm
5. Cihaza gönder
6. Haberleri çek
7. Kitaplık
8. Cihaz
9. Diske kaydet
10. Bağlan/Paylaş
11. Kitapları Sil

2. Tercihler
3. Kataloglar
4. Search & sort
5. The search interface
6. Aramaları kaydetmek
7. Searching the full text of all books
8. Sanal kitaplıklar
9. Temporarily marking books
10. Metadata’nın dosya isimlerinden tahmini
11. Kitap detayları
12. Tag browser
13. Kapak ızgarası
14. Cover browser
15. Adding notes for authors, series, etc.
16. Hızlıgörünüm
17. Görevler
18. Klavye kısayolları

Sık kullandığınız haber sitesini ekleme

1. Tamamı otomatik alınıyor

1. Calibre blog

2. bbc.co.uk

2. Getirme işleminin özelleştirilmesi

1. bbc.co.uk nin yazdırma sürümünün kullanılması

2. Makale biçemlerinin değiştirilmesi
3. Parçalama
4. Gerçek dünya örneği

3. Yeni reçeteler geliştirmek için ipuçları

4. İleri okuma

5. API belgelendirmesi

1. API documentation for recipes

1. BasicNewsRecipe

eKitap Görüntüleyici

1. eKitap Görüntüleyici’yi Başlatmak

2. Bir eKitapta Gezinmek

1. Yer İmleri
2. İçindekiler Tablosu
3. Konuma Göre Gezinme
4. Kaynak kipi

3. Highlighting text
4. Read aloud
5. Searching the text
6. Following links using only the keyboard

7. Okuma deneyiminizin görünümünü özelleştirmek

8. Sözlük araması
9. Metin ve Görüntü Kopyalamak
10. Zooming in on images
11. Klavye kısayolları
12. Non re-flowable content

13. Designing your book to work well with the calibre viewer

E-kitap dönüşümü

1. Giriş

2. Görünüm

1. Fontlar
2. Metin
3. Yerleşim
4. Stil
5. Stilleri dönüştür
6. Transform HTML

3. Sayfa yapısı
4. Heuristic processing
5. Search & replace

6. Structure detection

1. Bölümler ve sayfa sonları

2. Muhtelif

7. İçindekiler

8. HTML girdi belgelerini dönüştürürken resimler bölüm başlığı olarak kullanılıyor

9. İçindekiler bölümündeki girdilere metin temin etmek için etiket öznitelikleri kullanılıyor

10. How options are set/saved for conversion

11. Biçime özel ipuçları

1. Microsoft Word belgelerini dönüştür

1. Eski .doc dosyaları

2. TXT belgelerini dönüştür
3. PDF belgelerini dönüştür
4. Comic book collections
5. EPUB gelişmiş biçimleme tanıtımı
6. ODT belgelerini dönüştür

7. PDF’e dönüştürme

1. Başlıklar ve Altbilgiler

2. Yazdırılabilir İçindekiler

3. Custom page margins for individual HTML files

E-kitap düzenleme

Temel iş akışı

The File browser

1. Dosyaları yeniden adlandırma
2. Dosyaları birleştirme
3. Metin dosyası sırası değiştirme
4. Kapağı işaretleme
5. Dosyaları siliyor
6. Exporting files

7. Yeni resim/yazı tipi/vs. ekleme veya yeni boş dosyalar oluşturma

8. Dosyaları yer değiştirme

9. Biçim sayfalarını HTML dosyalarına etkin biçimde bağlama

Search & replace

1. Kaydedilmiş aramalar
2. İşlev kipi
3. Search ignoring HTML tags

Otomatik araçlar

1. Editing the Table of Contents
2. Checking the book
3. Adding a cover
4. Kaynak yazı tipleri katıştırılıyor

5. Katıştırılmış yazı tiplerinin alt kümelere ayrılması

6. Noktalamanın daha akıllı hale getirilmesi
7. Transforming CSS properties
8. Kullanılmayan CSS kurallarını kaldırma
9. Fixing HTML
10. Dosyaların güzelleştirilmesi
11. Inserting an inline Table of Contents
12. Setting Semantics
13. Filtering style information
14. Upgrading the book’s internals

Kontrol noktaları

The Live preview panel

1. HTML dosyalarının ayrılması

Canlı CSS paneli

Miscellaneous tools

1. The Table of Contents view

2. Kitaptaki kelimelerin yazım denetimi

1. Yeni sözlük ekleme

3. Özel karakter eklemek
4. Kod denetleme görünümü
5. Checking external links
6. Downloading external resources
7. Arranging files into folders by type

8. Diğer e-kitap biçimlerindeki dosyaları EPUB olarak içe aktarma

9. Raporlar aracı

Kod düzenleyicideki özel özellikler

1. Sözdizim vurgulama
2. İçeriğe duyarlı yardım
3. Otomatik tamamlama
4. Kesitler

Calibre İçerik sunucusu

1. İçerik sunucusuna diğer cihazlardan erişme

1. Sunucuya ev ağınızdaki cihazlardan erişme

1. Ev ağı bağlantısında sorun giderme

2. İnternetin olduğu her yerden sunucuya erişim

2. Sunucu arayüzü

1. Kitap listesi

2. Kitap okuyucusu

3. Tarayıcı desteği

4. Çevrimdışı desteği etkinleştirme

5. Managing user accounts from the command-line only

6. Integrating the calibre Content server into other servers

1. Using a full virtual host

2. Using a URL prefix

7. Creating a service for the calibre server on a modern Linux system

E-kitapları karşılaştır

1. Karşılaştırma görünümünü anlamak

2. Karşılaştırma aracı çalıştırılıyor

1. İki e-kitap dosyasının karşılaştırılması

2. ORIGINAL_FMT ile FMT dosyalarının karşılaştırılması

3. Bir kontrol noktasını ile kitabın mevcut durumunu düzenlerken karşılaştırmak

E-kitap meta verilerini düzenle

1. Bir defada bir kitabın üst verisini düzenlemek

1. Üst veri indiriliyor
2. Kitap biçimlerini yönetmek
3. Kapaklar hakkında

2. Birden fazla kitabın üst verisini birden değiştirmek

1. Ara ve değiştir

2. Metadata’nın toplu indirilmesi

3. Bir kitaba fazladan veri dosyaları ekleme

Sıkça Sorulan Sorular

E-book format conversion

1. Calibre hangi biçimlerde dönüştürmeyi destekler?

2. Dönüştürülecek en iyi kaynak biçimleri nelerdir?

3. Bir PDF dosyası dönüştürdüm, ama sonuçta çeşitli sorunlar var?

4. İngiliz alfabesi dışında karakterler, veya akıllı tırnak içeren dosyamı nasıl dönüştürürüm?

5. MOBI dosyalarındaki İçindekiler meselesi nedir?

6. Toplu HTML dosyalarını belirli bir sırada nasıl dönüştürürüm?

7. Calibre ile ürettiğim EPUB geçerli değil?

8. Dönüştürme araçlarının bazı gelişmiş özelliklerini nasıl kullanırım?

Device integration

1. Calibre hangi aygıtları destekliyor?

2. Aygıtımın calibre tarafından desteklenmesi konusunda nasıl yardım alabilirim?

3. Aygıtım calibre tarafından algılanmıyor?

4. Aygıtım standart dışı veya nadir. Bağlamak için ne yapabilirim?

5. Calibre’yi iPad/iPhone/iPod touch ile nasıl kullanırım?

1. Using the Content server

6. How do I use calibre with my Android phone/tablet or Kindle Fire?

1. USB kablo kullanımı

2. Kablosuz olarak

7. Kindle veya başka bir okuyucu aygıttaki web tarayıcıyı kullanarak calibre kitaplarıma erişebilir miyim?

8. Calibre kullanarak e-posta gönderemiyorum?

9. My device is getting mounted read-only in Linux, so calibre cannot connect to it?

10. Calibre neden Kindle koleksiyonlarını veya Nook raflarını desteklemiyor?

11. Calibre’yi Kobo Touch/Glo/vs. ile kullanırken hata alıyorum?

12. Covers for books I send to my e-ink Kindle show up momentarily and then are replaced by a generic cover?

13. MOBI dosyaları için kapaklar PC için Kindle/Android için Kindle/iPad vs. de görünmemeye başladı.

14. Calibre kullanarak bazı kitapları Kindle’me yükledim ama görünmüyorlar?

Library management

1. Kitap dosyaları nerede saklanıyor?

2. Calibre yazar adlarını ve sıralamayı nasıl yönetiyor?

3. Why doesn’t calibre let me store books in my own folder structure?

4. Neden calibre’nin x için sütunu yok?

5. Biçimleri veya ISBN’i gönderek bir sütunum olabilir mi?

6. Calibre kitaplığı verimi bir bilgisayardan diğerine nasıl aktarabilirim?

7. Calibre’deki kitap listesi boş!

8. Bir ağ sürücüsünde/NAS’da calibre kitaplığımla ilgili hatalar alıyorum?

Muhtelif

1. Amazon is stopping email delivery of MOBI files?

2. Calibre’nin sık kullanılan haber sitemden haberleri indirmesini istiyorum.

3. Neden calibre ismi?

4. Why does calibre show only some of my fonts on macOS?

5. calibre Windows’da başlamıyor?

6. calibre sık sık donuyor/çöküyor?

7. The calibre E-book viewer and Edit book tools do not work on Windows?

8. Görüntüleyiciyi kullanmak ya da herhangi bir dönüştürme yapmak Windows üzerinde izin verilmedi hatasına yol açıyor

9. calibre is not starting/crashing on macOS?

10. I get only a black or white screen when running the calibre E-book viewer?

11. Yükleyiciyi indirdim ama çalışmıyor?

12. Antivirüs programım calibre’nin bir virüs/trojen olduğunu söylüyor?

13. Calibre’yi nasıl yedeklerim?

14. Satın aldığım EPUB kitapları calibre ile nasıl kullanırım (veya .acsm dosyalarıyla ne yapacağım)?

15. “İzin Verilmedi” hatası alıyorum?

16. Yorum metadata’sının okuyucumda görüntülenmesini sağlayabilir miyim?

17. Calibre’nin HTTP vekilimi kullanmasını nasıl sağlarım?

18. Calibre’ye bir özelliğin eklenmesini istiyorum. Ne yapabilirim?

19. Calibre’nin neden otomatik güncellemesi yok?

20. Calibre’nin lisansı nedir?

21. Calibre’yi USB diskimden nasıl çalıştırırım?

22. How do I run parts of calibre like news download and the Content server on my own Linux server?

Rehberler

Sık kullandığınız haber sitesini ekleme

1. Tamamı otomatik alınıyor

1. Calibre blog

2. bbc.co.uk

2. Getirme işleminin özelleştirilmesi

1. bbc.co.uk nin yazdırma sürümünün kullanılması

2. Makale biçemlerinin değiştirilmesi
3. Parçalama
4. Gerçek dünya örneği

3. Yeni reçeteler geliştirmek için ipuçları

4. İleri okuma

5. API belgelendirmesi

1. API documentation for recipes

Kitap alt gruplarını yönetim, örneğin “tür”

1. Kurulum
2. Aranıyor
3. Kısıtlamalar
4. Useful template functions

XPath Rehberi

1. Etiket ismine göre seçim
2. Özniteliğe göre seçmek
3. Etiket içeriğine göre seçmek
4. Örnek e-kitap
5. XPath tümleşik fonksiyonları

Calibre şablon dili

1. Basic templates
2. Gelişmiş biçimleme
3. Using templates to define custom columns
4. Templates and plugboards

5. Using functions in templates - Single Function Mode

6. General Program Mode

7. More complex programs in template expressions - Template Program Mode

8. Python Template Mode

9. Stored templates

10. Providing additional information to templates

11. Notes on the difference between modes

12. User-defined Python template functions

13. Kayıt et/gönder şablonları ile ilgili özel notlar

14. Tips

15. Function reference

1. Reference for all built-in template language functions

Calibre’de kurallı ifadelerin kullanımına dair her şey

1. Öncelikle, bir uyarı ve bir kaç cesaret kelimesi

2. Calibre’de nerelerde düzenli ifadeler kullanabilirsiniz?

3. Yahu nedir bu düzenli ifade?

4. Açıklamak istemez misin?

5. Çok kötü görünmüyor. Sırada ne var?

6. Güzel! Mantıklı gelmeye başladı!

7. Bu özel karakterler değişik ve temiz, ama ya bir nokta ya da soru işareti eşleştirmek istersem?

8. Peki, en kullanışlı kümeler hangileri?

9. Ama bir kaç çeşit karakter dizim varsa, işler karmaşıklaşıyor?

10. Şunu atladın…

11. Başlangıçta, düzenli ifadeleri büyük küçük harf bağımsız yapabileceğimizi söylemiştin?

12. Galiba artık bu düzenli ifadeleri anlamaya başlıyorum… calibre’de bunları nasıl kullanırım?

1. Dönüştürmeler
2. Kitap Ekleme
3. Metadata’nın toplu düzenlenmesi

13. Quick reference

1. Quick reference for regexp syntax

14. Hazırlayanlar

Calibre’nin işlevselliğini artırmak için kendi eklentilerinizi yazmak

1. Bir calibre eklentisinin anatomisi

2. Kullanıcı Arayüzü eklentisi

1. __init__.py
2. ui.py
3. main.py
4. Eklenti ZIP dosyasından kaynak alma

5. Eklentiniz için kullanıcı yapılandırmasını etkinleştirmek

3. Kitap eklentilerini düzenle

1. main.py

4. Eklentinize çeviriler eklemek
5. Eklenti API’si
6. Eklentilerde hata ayıklama
7. Daha fazla eklenti örneği
8. Eklentilerinizi başkalarıyla paylaşmak

E-kitaplarda matematik dizgisi

1. Matematik ifadeleri ile basit bir HTML dosyası

2. Daha fazla bilgi

AZW3 • EPUB • MOBI katalogları oluşturma

1. Katalog için kitap seçmek
2. İçerilen bölümler
3. Önekler
4. Hariç tutulan kitaplar
5. Hariç tutulan türler
6. Diğer seçenekler
7. Özel katalog kapakları
8. Ek yardım kaynakları

Sanal kitaplıklar

1. Sanal kitaplıklar oluşturma

1. Yararlı Sanal kitaplık örnekleri

2. Sanal kitaplıklarla çalışma
3. Aramalarda Sanal kitaplıkları kullanma
4. Ek kısıtlamalar kullanmak

The calibre:// URL scheme

1. Belirli bir kitaplığa geçiş

2. Calibre’de belirli bir kitabı göster

3. E-kitap görüntüleyicide belirli bir konumda belirli bir kitabı açın

4. Kitap aranıyor

5. Open a book details window on a book in some library

6. Open the notes associated with an author/series/etc.

7. URL parametrelerinin hex kodlaması

Calibre’yi özelleştirmek

1. Çevresel değişkenler

2. İnce Ayarlar

3. Simgelerin, şablonların ve benzerlerinin üzerine yazmak

4. Calibre için özel ikon teman oluşturuluyor

5. Eklentilerle calibre’yi özelleştirmek

Komut Satırı Arayüzü Documented commands
calibre

1. [seçenekler]

calibre-customize

1. [seçenekler]

calibre-debug

1. [seçenekler]

calibre-server

1. [seçenekler]

calibre-smtp

1. [seçenekler]

calibredb

1. GENEL SEÇENEKLER
2. list
3. add
4. remove
5. add_format
6. remove_format
7. show_metadata
8. set_metadata
9. export
10. catalog
11. saved_searches
12. add_custom_column
13. custom_columns
14. remove_custom_column
15. set_custom
16. restore_database
17. check_library
18. list_categories
19. backup_metadata
20. clone
21. embed_metadata
22. search
23. fts_index
24. fts_search

ebook-convert

1. GİRDİ AYARLARI
2. ÇIKTI AYARLARI
3. GÖR ve HİSSET
4. SEZGİSEL İŞLEME
5. BUL ve DEĞİŞTİR
6. YAPI ALGILAMA
7. İÇİNDEKİLER
8. ÜSTBİLGİ
9. HATA AYIKLA

ebook-edit

1. [seçenekler]

ebook-meta

1. [seçenekler]

ebook-polish

1. [seçenekler]

ebook-viewer

1. [seçenekler]

fetch-ebook-metadata

1. [seçenekler]

lrf2lrs

1. [seçenekler]

lrfviewer

1. [seçenekler]

lrs2lrf

1. [seçenekler]

web2disk

1. [seçenekler]

Undocumented commands

Calibre geliştirme ortamı ayarlamak

1. Tasarım felsefesi

1. Kod düzeni

2. Kodun alınması

1. Değişikliklerinizin içerilmesi için gönderilmesi

3. Windows geliştirme ortamı
4. macOS development environment
5. Linux geliştirme ortamı

6. Aynı bilgisayarda ayrı “normal” ve “geliştirme” calibre yüklemelerini bulundurmak

7. Hata ayıklama ipuçları

1. Print ifadeleri kullanarak

2. Using an interactive Python interpreter

3. Using the Python debugger as a remote debugger

4. Using the debugger in your favorite Python IDE

5. Executing arbitrary scripts in the calibre Python environment

8. Projelerinizde calibre kullanmak

1. Calibre’nin ikilik kurulumu

2. Linux üstünde kaynak kurulum

9. Calibre’nin çeşitli kısımları için API belgelendirmesi

1. API documentation for recipes

1. BasicNewsRecipe

2. Eklentiler için API belgeleri

1. Eklenti
2. FileTypePlugin
3. Metadata eklentileri
4. Katalog eklentileri
5. Metadata indirme eklentileri
6. Dönüştürme eklentileri
7. Aygıt sürücüleri
8. Kullanıcı arayüzü eylemleri
9. Preferences plugins

3. Veri tabanı arayüzü için API belgelendirmesi

1. Cache

4. E-kitap düzenleme araçları için API belgeleri

1. Kapsayıcı nesne

2. Bir kapsayıcıdaki bileşen dosyalarının yönetimi

3. Güzel yazdırma ve ayrıştırma hatalarının otomatik düzeltilmesi

4. Kitap ceketlerinin yönetimi
5. Dosyaların ayrılması ve birleştirilmesi
6. Kapakların yönetimi
7. CSS ile Çalışmak
8. İçindekiler ile Çalışmak
9. Kitap düzenleme aracı

10. Düzenleyicinin kullanıcı arayüzünün kontrolü

Dijital Haklar Yönetimi (DRM)

1. DRM kişisel olarak benim için ne ifade ediyor?

2. DRM yazarlar için ne yapar?

3. DRM ve özgürlük

4. Calibre neden DRM’yi desteklemiyor?

5. Calibre’nin içerik sağlayıcılar hakkındaki görüşü nedir?

6. DRM ile savaşmaya nasıl yardımcı olabilirim?

Açıklayıcı sözlük