chbrown / overdrive Goto Github PK
View Code? Open in Web Editor NEWBash script to download mp3s from the OverDrive audiobook service
Bash script to download mp3s from the OverDrive audiobook service
Hello!
Really looking forward to being able to use this tool, but when I enter the commands, I receive the following:
sudo chmod +x /usr/local/bin/overdrive
chmod: /usr/local/bin/overdrive: No such file or directory
I've tried separating out the commands (as you mentioned in another issue), as far as I can tell, but I always get stopped with the "No such file..." error.
Any thoughts on why this would be the case? Thanks so much for making this tool for those of us that updated to Catalina!
Some of the overdrive
commands call executables that are not standard on all systems.
When one of these is needed, but not found on the calling machine, the script should exit with a descriptive error message, indicating precisely what executable was missing (perhaps even describing how to install it).
Do with it as you wish. I still haven't updated this for python3, but it's really handy combined with https://github.com/ex-nerd/audiotools (wow, I feel old looking at the last-updated dates on those) to merge files into m4b
files that can load into a dedicated audiobook app on a phone.
#!/usr/bin/env python2
#
# Use with build_m4b from https://github.com/ex-nerd/audiotools
# Due to overdrive low quality, there is no point in encoding aac files
# with better than: 64kbps stereo, HE, optimize for voice
#
import os, sys, re
import mutagen.id3 as id3
from mutagen.mp3 import MP3
from mutagen import File
from collections import OrderedDict
def timestr(secs):
(secs, ms) = str(secs).split('.')
ms = float(ms[0:3] + '.' + ms[3:])
secs = int(secs)
hours = int(secs // 3600)
secs = secs % 3600
mins = int(secs // 60)
secs = secs % 60
return '{0:02}:{1:02}:{2:02}.{3:03.0f}'.format(hours, mins, secs, ms)
def load_mp3(total, dir, file):
path = os.path.join(dir, file)
#mfile = File(path)
#file = File('some.mp3') # mutagen can automatically detect format and type of tags
#artwork = file.tags['APIC:'].data # access APIC frame and grab the image
#with open('image.jpg', 'wb') as img:
# img.write(artwork) # write artwork to new image
#artwork = mfile.tags['APIC:'].data # access APIC frame and grab the image
#with open('{0}.jpg'.format(path), 'wb') as img:
# img.write(artwork) # write artwork to new image
audio = MP3(path)
print(audio.info.length) #, audio.info.bitrate
m = id3.ID3(path)
data = m.get('TXXX:OverDrive MediaMarkers')
if not data:
print("Can't find TXXX data point for {0}".format(file))
print(m.keys())
return
info = data.text[0].encode("ascii", "ignore")
#print info
file_chapters = re.findall(r"<Name>\s*([^>]+?)\s*</Name><Time>\s*([\d:.]+)\s*</Time>", info, re.MULTILINE)
chapters = []
for chapter in file_chapters:
(name, length) = chapter
name = re.sub(r'^"(.+)"$', r'\1', name)
name = re.sub(r'^\*(.+)\*$', r'\1', name)
name = re.sub(r'\s*\([^)]*\)$', '', name) # ignore any sub-chapter markers from Overdrive
name = re.sub(r'\s+\(?continued\)?$', '', name) # ignore any sub-chapter markers from Overdrive
name = re.sub(r'\s+-\s*$', '', name) # ignore any sub-chapter markers from Overdrive
name = re.sub(r'^Dis[kc]\s+\d+\W*$', '', name) # ignore any disk markers from Overdrive
name = name.strip()
t_parts = list(length.split(':'))
t_parts.reverse()
seconds = total + float(t_parts[0])
if len(t_parts) > 1:
seconds += (int(t_parts[1]) * 60)
if len(t_parts) > 2:
seconds += (int(t_parts[2]) * 60 * 60)
chapters.append([name, seconds])
print(name, seconds)
#chapters = re.search(r'(\w+)', info)
#print(repr(chapters))
return (total + audio.info.length, chapters)
return
# try:
# if file.decode("utf-8") == new.decode("utf-8"):
# new = None
# except:
# print " FILE: "+os.path.join(dirname, file)
# raise
# # Return
# return (m, new, changed)
def visit(arg, dirname, names):
print(dirname)
os.chdir(dirname)
#parent = os.path.dirname(dirname)
#thisdir = os.path.basename(dirname)
#print thisdir
# Parse the files
total = 0;
all_chapters = OrderedDict()
for file in sorted(names):
if file.endswith('.mp3'):
(total, chapters) = load_mp3(total, dirname, file)
for chapter in chapters:
if chapter[0] in all_chapters.keys():
continue
all_chapters[chapter[0]] = chapter[1]
if len(all_chapters) > 0:
with open('overdrive_chapters.txt', 'w') as file:
for name, length in all_chapters.items():
chapstr = u'{0} {1}'.format(timestr(length), name)
print(chapstr)
file.write(chapstr + '\n')
#print(repr(all_chapters))
if len(sys.argv) > 1:
path = os.path.abspath(sys.argv[1])
else:
path = os.path.abspath('.')
print(path)
os.path.walk(path, visit, None)
The download & return suddenly stopped working I have tried with a fresh install same problem a .odm.license is created by the script it's empty & nothing happens
Hello,
I recently started getting a License Error when trying to convert the odm files. This wasn't happening before.
userName-MBP Downloads % ~/.local/bin/overdrive download Red.odm
Generating random ClientID=C04D296C-73E8-4AD8-AE6D-912C6DEBF056
Using AcquisitionUrl=https://ofs.contentreserve.com/MP3LicenseAcquisitionService.svc/5aed893d-f197-4689-953a-af4100b28671
Using MediaID=416729fe-7fe4-43a5-84ec-10896faa9fc4-425
Using RawHash=C04D296C-73E8-4AD8-AE6D-912C6DEBF056|1.2.0|10.11.6|ELOSNOCAIDEMEVIRDREVO
Using Hash=3+10LjLJzc+88b6YMmgcdqWcHA0=
1003The requested license is either invalid or already acquired%
userName-MBP Downloads %
your assistance would be greatly appreciated.
Greetings,
Environment: Ubuntu 20.04
Script version: Dated 4/29/2020 (tried downloading latest copy, same problem exists)
(Edited issue to resolve formatting problem with metadata excerpt)
I don't know if this is a problem with Overdrive itself, or if something changed in one of the prerequisite tools, but I am having the following problem when downloading items from Overdrive (note that this occurs with multiple different items, I am using this one merely as an example - also note that client and license information has been removed, as there doesn't appear to be a problem with acquiring and using the license):
tracy@tracy-HP:/media/tracy/Refurb/LibAB$ overdrive download 'Buying Time.odm'
License already acquired: Buying Time.odm.license
Using License=<license info removed></License>
Using ClientID=<removed> from License
Using Author=Joe Haldeman,
Using Title=Buying Time-
Creating directory Joe Haldeman, - Buying Time-
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part01.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part01.mp3 successfully
Downloading Joe Haldeman, - Buying Time-/Buying Time--
Downloaded Joe Haldeman, - Buying Time-/Buying Time-- successfully
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part02.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part02.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part03.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part03.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part04.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part04.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part05.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part05.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part06.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part06.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part07.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part07.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part08.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part08.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Downloading Joe Haldeman, - Buying Time-/Buying Time--Part09.mp3
Downloaded Joe Haldeman, - Buying Time-/Buying Time--Part09.mp3 successfully
Output already exists: Joe Haldeman, - Buying Time-/Buying Time--
Using CoverUrl=https://images.contentreserve.com/ImageType-100/6578-1/%7BCD9D8596-387E-4F84-A4A3-8734E1F6D941%7DImg100.jpg
Downloading Joe Haldeman, - Buying Time-/folder.jpg
Downloaded cover image successfully
tracy@tracy-HP:/media/tracy/Refurb/LibAB$
Note on the Title and Author lines the spurious characters after the names (the comma after the author, and the dash after the title). Also note the repeated "Output already exists" lines (after each download except the first).
I have successfully used the script in the past (back in early May), but at that time I was running Ubuntu 19.10 (I did a clean install when I moved to 20.04, so it is possible that some underlying dependency changed, or even is missing).
Here are what I think are the relevant lines from the odm files (I can provide the entire odm file, if that will be helpful):
<![CDATA[<Metadata>
<ContentType>MP3 Audio Book</ContentType>
<Title>Buying Time</Title>
<SortTitle>Buying Time</SortTitle>
<Publisher>Novel Audio</Publisher>
<ThumbnailUrl>https://images.contentreserve.com/ImageType-200/6578-1/{CD9D8596-387E-4F84-A4A3-8734E1F6D941}Img200.jpg</ThumbnailUrl>
<CoverUrl>https://images.contentreserve.com/ImageType-100/6578-1/{CD9D8596-387E-4F84-A4A3-8734E1F6D941}Img100.jpg</CoverUrl>
<Creators>
<Creator role="Author" file-as="Haldeman, Joe">Joe Haldeman</Creator>
<Creator role="Narrator" file-as="Vale, Eric">Eric Vale</Creator>
</Creators>
And here is how the same data appears in the metadata file:
<Metadata>
<ContentType>MP3 Audio Book</ContentType>
<Title>Buying Time</Title>
<SortTitle>Buying Time</SortTitle>
<Publisher>Novel Audio</Publisher>
<ThumbnailUrl>https://images.contentreserve.com/ImageType-200/6578-1/{CD9D8596-387E-4F84-A4A3-8734E1F6D941}Img200.jpg</ThumbnailUrl>
<CoverUrl>https://images.contentreserve.com/ImageType-100/6578-1/{CD9D8596-387E-4F84-A4A3-8734E1F6D941}Img100.jpg</CoverUrl>
<Creators>
<Creator role="Author" file-as="Haldeman, Joe">Joe Haldeman</Creator>
<Creator role="Narrator" file-as="Vale, Eric">Eric Vale</Creator>
</Creators>
Any ideas what might be going wrong?
Kudos! I got this working on Ubuntu on WSL with Win10. Only notable issue I had was the overdrive.sh script has carriage returns as well as newlines. I ran it through sed to delete the returns (s/\r//) and now it seems to be working.
Current behavior is just to assume all supplied paths are good; if a path is missing, xmllint
will fail with a cryptic error message:
$ overdrive download DoesNotExist.odm
Generating random ClientID=B49967C8-61F8-4365-88E0-F5C822B13235
warning: failed to load external entity "DoesNotExist.odm"
This is an unusual error case, but when it does arise, the fatal "warning" can be confusing / misleading, and the proper solution of explicitly checking for existence as a preliminary step is trivial.
See, e.g., #40, comments 3–4.
The --fail
flag in curl -o my.out --fail ...
handily avoids writing invalid responses to my.out
when the HTTP status code is an error, but unfortunately also causes the response body to be completely omitted, making it difficult to determine what the problem was.
AFAIK, there is no combination of curl
flags to treat response content distinctly differently based on the returned HTTP status being an error or not. Ideally, I'd like to:
HTTP 200 OK
(or any 2**
code, ideally), write response body to given output file and return exit code 0
so that the script (running with set -e
) continues apaceThat's pretty much what currently happens with -o ... --fail
, except for the bold bit.
The script should check (e.g. command -v xyz >/dev/null 2>&1
) for all executables listed in https://github.com/chbrown/overdrive#prerequisites, and exit with a descriptive error if any are missing, rather than failing mid-stride and leaving the user confused and in an incomplete state (leading to #33, among others).
Either by checking the file contents or the result of the curl call in acquire_license
.
(Debugging upstream errors requires a tedious extra step if the license fails; see #3)
The script returns a License error since the command uuid is not available in mac OS Mojave (10.14) and can't be easily installed via brew.
There is a similar command available uuidgen that provides the same functionality (generating a random UUID).
A simple solution is to create an alias in bash uudi -> uuidgen or replace the single use of uuid in the script by uuidgen
I used this in Linux, but does this work in Windows too?
If it doesn't, can anyone recommend a Windows program that works.
You don't need to use mutagen for tagging, FFMPEG will work just fine using the -metadata
command.
It's all spelled out pretty well in this guide: https://gist.github.com/eyecatchup/0757b3d8b989fe433979db2ea7d95a01
(note: not the official user guide, but arguably better written than the official docs on this specific topic).
Hi, great project you have here.
I noticed that you updated the readme on the topic of Libby the other day. I'm not trying to steal attention away from this project, I just wanted to show you what I've been working on lately now that it's clear that the legacy OverDrive app is being discontinued in early 2023 and odm's might go away.
It's a CLI for Libby written in python. It can download audiobooks from Libby without the odm the same way as their online app does.
I mainly enjoy the part of programming that involves figuring out how stuff works, not so much the maintenance part, so please feel free to fork.
First of all, thank you for this awesome program. It works so much better than WINE's overdrive app on linux.
Just one small suggestion (you don't need to do it but it might help some people). For the 'Prerequisites' section, add this to make it easier for people to figure out the actual package names to install. Another idea is to list one liner commands for each major OS (mac, windows, debian, red hat, arch, etc)
overdrive.sh successfully downloads 6 out of 10 parts but then tells me "...metadata:1: parser error : Document is empty". I realized from the first error output that the command "tidy" was not installed, so I installed it and tried overdrive.sh again, no dice. Here is the output:
ryan@pocketwee:~/Books$ overdrive download DontBurnThisBookThinkingforYourselfinAnAgeofUn-683.odm
License already acquired: DontBurnThisBookThinkingforYourselfinAnAgeofUn-683.odm.license
Using License=<License xmlns="http://license.overdrive.com/2008/03/License.xsd" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><SignedInfo><Version>1.0</Version><ContentID>d1ef3dc1-c3b0-4a09-a89d-fbb31252a84a-425</ContentID><ClientID>E54A3A57-40E2-4F2F-928D-EA665B9B5872</ClientID></SignedInfo><Signature>MmaVqIzcTm5sA8pbZoGYbt4DonXUiG+fm+Cwp9lGS7c3CnxM/OYArYqi5lH40mfXdTcT0hQPYF5F2avDdHE/EAqczJus9fssGMBgy946DtTH8ca+pCuX1tZExctFOmIrQPxnhIdMkB4IJMd4MZVYhSZyKmm/8PDhq2HzAcRnn6o=</Signature></License>
Using ClientID=E54A3A57-40E2-4F2F-928D-EA665B9B5872 from License
DontBurnThisBookThinkingforYourselfinAnAgeofUn-683.odm.metadata:1: parser error : Document is empty
^
seq: invalid floating point argument: ‘’
Try 'seq --help' for more information.
Using Author=
DontBurnThisBookThinkingforYourselfinAnAgeofUn-683.odm.metadata:1: parser error : Document is empty
^
Using Title=
Creating directory -
Output already exists: - /Part01.mp3
Output already exists: - /Part02.mp3
Output already exists: - /Part03.mp3
Output already exists: - /Part04.mp3
Output already exists: - /Part05.mp3
Output already exists: - /Part06.mp3
DontBurnThisBookThinkingforYourselfinAnAgeofUn-683.odm.metadata:1: parser error : Document is empty
^
Using CoverUrl=
Cover image not available
ryan@pocketwee:~/Books$
Hi. I've really enjoyed using your script over the last couple of years.
Have you, by chance, looked at the new Libby app? It looks like Overdrive is going away in January 2023 and Libby doesn't allow direct downloading of any files.
Just wondered if you've given it any thought?
Thanks
I don't know much about the back end of how this works, I'm admittedly very new to this world but this whole project has encouraged me to learn how to use bash scripts and python so I'm slowly getting a feel for it.
I've written a script that allows me to decipher the table of contents from the overdrive "listen in browser" feature and extract the time stamps to use as label markers to edit chapters, but that process still forces me to manually enter into the inspect element for each book. Curious if there's any way to pull that information out at the same time we're pulling the downloads.
Or if that just doesn't make sense for this specific project I'll find another solution. Thanks for all you do!
Thanks for putting together this script....
I have a couple of audio books checked out with .odm files on my computer. Every time I run overdrive download command I get this:
Generating random ClientID=AD447C67-5C73-4916-9F5E-92B680865419
Using AcquisitionUrl=https://ofs.contentreserve.com/MP3LicenseAcquisitionService.svc/158190f9-969d-4f8d-b8e3-ad8700b591a5
Using MediaID=e3cb0c48-c689-4515-9aeb-a6f0cf4bf425-425
Using RawHash=AD447C67-5C73-4916-9F5E-92B680865419|1.2.0|10.11.6|ELOSNOCAIDEMEVIRDREVO
Using Hash=1mwwKSyHYpTMxkPIccML3GJLIY0=
Using License=1.0e3cb0c48-c689-4515-9aeb-a6f0cf4bf425-425AD447C67-5C73-4916-9F5E-92B680865419UaD0d2lqOJos6mQiYUkn4LRSr+ilWRsnWoAN/NB98PTGDmcZbf2fEYZj9lO4s5dYkYzzH+h6HOXzBaaen9JNSwcl1fp4qwpnl3EPBaZ1GvSaOEPafpJnTO9Kd74P/lx92vNGzLvpn+ivS8j01kkcLRAFjuFhaSyaG73pcr1FLlM=
Using ClientID=AD447C67-5C73-4916-9F5E-92B680865419 from License
/home/user/.local/bin/overdrive: line 127: tidy: command not found
audiobookfilename.odm.metadata:1: parser error : Document is empty
(my user directory and odm file name changed for privacy but they are correct in the original output.
I have no idea how to interpret that... Any ideas?
delete.
as many people already know, overdrive is being phased out after years of being assured that both overdrive and libby will continue side by side. anyways, this is the screen on libby via their website.
This means when overdrive will sunset, we will be left with no way to download mp3s. Has anyone found a way to de-drm libby files?
I'm using LazyLibrarian to replenish my audiobook library which I lost a little while ago, and while it's pretty good at giving a graphical display of a lot of the content, it's woeful at organising the metadata (using GoodReads as its primary source of truth, which helps for book details, but is rubbish for things like chapters etc).
Using OverDrive appears to be a natural means to resolve that, both in terms of grabbing great quality files, as well as well managed files in terms of chapters etc.
LazyLibrarian has the options to blend files together, and uses a range of options (including FFMPEG) to do so, but still relies on the file data from somewhere.
So I think it would be great to get these two working in harmony together.
I see you have no LICENSE file for this project. The default is copyright.
I would suggest releasing the code under the GPL-3.0-or-later or AGPL-3.0-or-later license so that others are encouraged to contribute changes back to your project.
It's be nice if tabbing autocomplete was added for commends.
i.e.
overdrive do
[TAB] >> overdrive download
overdrive re
[TAB] >> overdrive return
Not sure what happened, can you help?
After installing and then trying to download the book, it tells me "zsh: command not found: overdrive". I'm closed Terminal and reinstalled the first command with the same result. Help please.
Hi @chbrown, first of all, thank you for doing this. I'm sure once I get it going correctly, this will be such an amazing tool to have.
I don't have any experience coding or using Terminal, so it's more than likely I'm not doing something right, but when I copy/paste the code listed on your GitHub, I get the following:
% mkdir -p ~/.local/bin
curl https://chbrown.github.io/overdrive/overdrive.sh -o ~/.local/bin/overdrive
chmod +x ~/.local/bin/overdrive
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 8911 100 8911 0 0 27760 0 --:--:-- --:--:-- --:--:-- 27760
Then I move on to step 2 and type overdrive
and get the following:
Usage: overdrive [-h|--help]
overdrive --version
overdrive command [command2 ...] book.odm [book2.odm ...] [-v|--verbose]
Commands:
download Download the mp3s for an OverDrive book loan.
return Process an early return for an OverDrive book loan.
info Print the author, title, and total duration (in seconds) for each OverDrive loan file.
metadata Print all metadata from each OverDrive loan file.
You must supply at least one command.
You must supply at least one media file (the .odm extension is required).
So all seems to be working well. But then I input the download code and get the following:
% cd ~/Downloads
overdrive download LilyandtheOctopus-9077.odm
License already acquired: LilyandtheOctopus-9077.odm.license
Using License=
LilyandtheOctopus-9077.odm.license:1: parser error : Document is empty
^
I have the .license file that goes along with the .odm file, but nothing happens at this point and I can't seem to figure out a way to fix anything.
Any help would be greatly appreciated!
I've noticed that all the audio MP3 files downloaded via this script are very low volume. What's odd is that when I listen to the book in Libby or Overdrive's other apps, the volume is perfectly fine. (Libby at 60% volume on my iPhone requires 95% or 100% volume) Is there a way to adjust this in the script? (E.g. changing the encoding options?)
I try to download the .odm file, and I am getting this error message:
-bash: cd: /Users/BethAnne1/Downloads/Users/BethAnne1/Downloads/UnbrokenTheYoungAdultAdaptationAnOlympiansJour-584.odm: No such file or directory
Program was interrupted during a download and upon rerunning, the last partially downloaded file was skipped because it already existed, even though it was not completely downloaded. Instead of just checking if the file exists, you need to also check the byte count. Probably could just let curl handle that by passing it --continue-at -
.
Hey chbrown! I was an avid Overdrive user and I'm using your overdrive thingie to brace myself for the upgrade to Catalina which breaks Overdrive. I'm a noob to GitHub and pretty much even Terminal, but I'm fearless. I followed the instructions to install it, borrowed a book an downloaded the .odm. Issued the command and got the xmlstarlet command not found. Maybe there's some kind of developer commands I don't have installed. Can you help?
I created a gist but I'm not sure of the proper protocol for giving it to you so I'll include the embed link and the verbose text itself.
<script src="https://gist.github.com/AlpacaLips99/212e18504eec99f1bdbb2b9f4269d82f.js"></script>Corys-MacBook-Pro:Downloads coryengel$ overdrive download WakingGodsThemisFilesSeriesBook02-63028.odm --verbose
I got the ODM from my library's overdrive website, but the overdrive script no longer works. Is this overdrive script dead, now that the Overdrive app no longer works?
Yes, I did read the section on "Libby" in the readme (https://github.com/chbrown/overdrive#libby), but just wanted to confrim.
I borrowed The Hitchhiker's Guide to the Galaxy
and downloaded the mp3 files. There are 5 chapters in the odm file (copied below) but the script only downloaded 4 files. I suspect it is due to the missing newline at the end of the output from the extract_filenames
function.
Here's is the odm file:
<?xml version="1.0"?>
<OverDriveMedia id="1c96507d-f48d-4fef-b190-130ce15c8fa8-425" ODMVersion="3.0.0.0" OMCVersion="3.0.0.0">
<License>
<AcquisitionUrl>https://ofs.contentreserve.com/MP3LicenseAcquisitionService.svc/a9368c50-6b29-44d3-bed2-ab8900003648</AcquisitionUrl>
</License><![CDATA[<Metadata>
<ContentType>MP3 Audio Book</ContentType>
<Title>The Hitchhiker's Guide to the Galaxy</Title>
<SubTitle>The Hitchhiker's Guide to the Galaxy Series, Book 1</SubTitle>
<SortTitle>Hitchhikers Guide to the Galaxy The Hitchhikers Guide to the Galaxy Series Book 01</SortTitle>
<Publisher>Penguin Random House Audio Publishing Group</Publisher>
<Series>The Hitchhiker's Guide to the Galaxy</Series>
<ThumbnailUrl>https://images.contentreserve.com/ImageType-200/1191-1/{1C96507D-F48D-4FEF-B190-130CE15C8FA8}Img200.jpg</ThumbnailUrl>
<CoverUrl>https://images.contentreserve.com/ImageType-100/1191-1/{1C96507D-F48D-4FEF-B190-130CE15C8FA8}Img100.jpg</CoverUrl>
<Creators>
<Creator role="Author" file-as="Adams, Douglas">Douglas Adams</Creator>
<Creator role="Narrator" file-as="Fry, Stephen">Stephen Fry</Creator>
</Creators>
<Subjects>
<Subject id="26">Fiction</Subject>
<Subject id="80">Science Fiction</Subject>
<Subject id="98">Science Fiction & Fantasy</Subject>
</Subjects>
<Languages>
<Language code="en">English</Language>
</Languages>
<Description><b><i>NEW YORK TIMES </i>BESTSELLER • "Extremely funny . . . inspired lunacy . . . [and] over much too soon."—<i>The Washington Post Book World</i></b><br><b>Nominated as one of America's best-loved novels by PBS's <i>The Great American Read</i></b><br>Seconds before Earth is demolished to make way for a galactic freeway, Arthur Dent is plucked off the planet by his friend Ford Prefect, a researcher for the revised edition of <i>The Hitchhiker's Guide to the Galaxy </i>who, for the last fifteen years, has been posing as an out-of-work actor.<br>Together, this dynamic pair began a journey through space aided by a galaxyful of fellow travelers: Zaphod Beeblebrox—the two-headed, three-armed ex-hippie and totally out-to-lunch president of the galaxy; Trillian (formerly Tricia McMillan), Zaphod's girlfriend, whom Arthur tried to pick up at a cocktail party once upon a time zone; Marvin, a paranoid, brilliant, and chronically depressed robot; and Veet Voojagig, a former...</Description>
</Metadata>]]><DrmInfo>
<PlayOnPC>1</PlayOnPC>
<PlayOnPCCount>-1</PlayOnPCCount>
<BurnToCD>1</BurnToCD>
<BurnToCDCount>-1</BurnToCDCount>
<PlayOnPM>1</PlayOnPM>
<TransferToSDMI>1</TransferToSDMI>
<TransferToNonSDMI>1</TransferToNonSDMI>
<TransferCount>-1</TransferCount>
<CollaborativePlay>0</CollaborativePlay>
<PublicPerformance>0</PublicPerformance>
<TranscodeToAAC>1</TranscodeToAAC>
<ExpirationDate>2020-04-15T04:00:37Z</ExpirationDate><Hash>psH5vw10Ee2JHYeevPcyybsMdGk=</Hash><Hash2>7N3xlqEPZMb2WghFVIRuRBkn1Ak=</Hash2></DrmInfo><Formats><Format name="Medium Quality"><Quality level="Medium" /><Protocols><Protocol method="download" baseurl="https://mp3audio-gk.cdn.overdrive.com/MP3AudioStore1" /></Protocols><Parts count="5"><Part number="1" filesize="36500043" name="Part 1" filename="1191-1\1C9\650\7D\{1C96507D-F48D-4FEF-B190-130CE15C8FA8}Fmt425-Part01.mp3" duration="75:54" /><Part number="2" filesize="34837193" name="Part 2" filename="1191-1\1C9\650\7D\{1C96507D-F48D-4FEF-B190-130CE15C8FA8}Fmt425-Part02.mp3" duration="72:26" /><Part number="3" filesize="33988318" name="Part 3" filename="1191-1\1C9\650\7D\{1C96507D-F48D-4FEF-B190-130CE15C8FA8}Fmt425-Part03.mp3" duration="70:40" /><Part number="4" filesize="31996742" name="Part 4" filename="1191-1\1C9\650\7D\{1C96507D-F48D-4FEF-B190-130CE15C8FA8}Fmt425-Part04.mp3" duration="66:31" /><Part number="5" filesize="31771462" name="Part 5" filename="1191-1\1C9\650\7D\{1C96507D-F48D-4FEF-B190-130CE15C8FA8}Fmt425-Part05.mp3" duration="66:03" /></Parts></Format></Formats><Source id="SanJose"><Name>San José Digital Library</Name><WebsiteUrl>http://overdrive.sjlibrary.org</WebsiteUrl><BannerUrl>http://overdrive.sjlibrary.org/ODMBanner.gif</BannerUrl><AccentColor>#eeeeee</AccentColor></Source><TransactionID>022-1693196-00045</TransactionID><EarlyReturnURL>https://notifications-ofs.contentreserve.com/EarlyReturn/SanJose/022-1693196-00045/1c96507d-f48d-4fef-b190-130ce15c8fa8-425?h=3sMV0nvFR5zf%2fqEj9DvuFbXnRbf9Lbz2vdouyzTJ5t8%3d</EarlyReturnURL><DownloadSuccessURL>https://notifications-ofs.contentreserve.com/DownloadSuccess/SanJose/022-1693196-00045/1c96507d-f48d-4fef-b190-130ce15c8fa8-425?h=3sMV0nvFR5zf%2fqEj9DvuFbXnRbf9Lbz2vdouyzTJ5t8%3d</DownloadSuccessURL></OverDriveMedia>```
getting an error when trying to use this
looks amazing! hope you can help. here's the debug log below:
do you know what this means?
~/D/b/odm ❯❯❯ overdrive download multipart.odm --verbose
+ printf 'Entering debug (verbose) mode\n'
Entering debug (verbose) mode
+ shift
+ [[ 0 -gt 0 ]]
+ [[ 1 -eq 0 ]]
+ [[ 1 -eq 0 ]]
+ HEADER_PRINTED=
+ for ODM in '"${MEDIA[@]}"'
+ for COMMAND in '"${COMMANDS[@]}"'
+ case $COMMAND in
+ download multipart.odm
+ license_path=multipart.odm.license
+ acquire_license multipart.odm multipart.odm.license
+ [[ -e multipart.odm.license ]]
++ uuid
++ tr /a-z/ /A-Z/
/usr/local/bin/overdrive: line 66: uuid: command not found
+ ClientID=
+ printf 'Generating random ClientID=%s\n' ''
Generating random ClientID=
++ xmlstarlet sel -t -v /OverDriveMedia/License/AcquisitionUrl multipart.odm
/usr/local/bin/overdrive: line 70: xmlstarlet: command not found
+ AcquisitionUrl=
After running
overdrive download myBook.odm
The .license is created and I get this error:
Using License=<LicenseError xmlns="http://license.overdrive.com/2008/03/LicenseError.xsd" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorCode>1001</ErrorCode><ErrorMessage>Missing Query Parameter</ErrorMessage><ErrorDetails i:nil="true"/></LicenseError>
Any ideas what's going on and how can I fix it?
Hi @chbrown,
Thank you for creating an amazing OverDrive script that is so helpful! I am getting the following error when I try and download an .odm file. I did not see others with a similar issue. I have tried a few .odm files from my local library. Here are the commands and responses. I also attached the .odm file (not I had to change the extension to .zip to upload, but you can just change it back to .odm).
MacBook-Air:Downloads kmesser$ ~/.local/bin/overdrive download Downloads/Sentence.odm
/Users/kmesser/.local/bin/overdrive: line 1: syntax error near unexpected token `newline'
/Users/kmesser/.local/bin/overdrive: line 1: `<!DOCTYPE html>'
Thanks so much for the help!
Sentence.zip
Adam
Novice here - but why do I have to reinstall the script each time I use it. Am I doing something incorrectly when I install it?
On MacOS
user@MBP Audiobooks % overdrive download HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm --verbose
Entering debug (verbose) mode
+ CURLOPTS=("${CURLOPTS[@]:1}")
+ shift
+ [[ 0 -gt 0 ]]
+ [[ 1 -eq 0 ]]
+ [[ 1 -eq 0 ]]
+ HEADER_PRINTED=
+ for ODM in '"${MEDIA[@]}"'
+ for COMMAND in '"${COMMANDS[@]}"'
+ case $COMMAND in
+ download HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm
+ license_path=HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm.license
+ acquire_license HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm.license
+ [[ -e HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm.license ]]
++ uuidgen
++ tr '[:lower:]' '[:upper:]'
+ ClientID=C426F217-3A9C-4C7A-9B1F-A490C0E7D77F
+ printf 'Generating random ClientID=%s\n' C426F217-3A9C-4C7A-9B1F-A490C0E7D77F
Generating random ClientID=C426F217-3A9C-4C7A-9B1F-A490C0E7D77F
++ xmllint --xpath '/OverDriveMedia/License/AcquisitionUrl/text()' HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm
+ AcquisitionUrl=https://ofs.contentreserve.com/MP3LicenseAcquisitionService.svc/e610fc69-54f9-4ce5-aa85-acad00e8cf8c
+ printf 'Using AcquisitionUrl=%s\n' https://ofs.contentreserve.com/MP3LicenseAcquisitionService.svc/e610fc69-54f9-4ce5-aa85-acad00e8cf8c
Using AcquisitionUrl=https://ofs.contentreserve.com/MP3LicenseAcquisitionService.svc/e610fc69-54f9-4ce5-aa85-acad00e8cf8c
++ xmllint --xpath 'string(/OverDriveMedia/@id)' HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm
+ MediaID=12be9ca6-db3c-4013-a000-fce166918282-425
+ printf 'Using MediaID=%s\n' 12be9ca6-db3c-4013-a000-fce166918282-425
Using MediaID=12be9ca6-db3c-4013-a000-fce166918282-425
+ RawHash='C426F217-3A9C-4C7A-9B1F-A490C0E7D77F|1.2.0|10.11.6|ELOSNOC*AIDEM*EVIRDREVO'
+ printf 'Using RawHash=%s\n' 'C426F217-3A9C-4C7A-9B1F-A490C0E7D77F|1.2.0|10.11.6|ELOSNOC*AIDEM*EVIRDREVO'
Using RawHash=C426F217-3A9C-4C7A-9B1F-A490C0E7D77F|1.2.0|10.11.6|ELOSNOC*AIDEM*EVIRDREVO
++ echo -n 'C426F217-3A9C-4C7A-9B1F-A490C0E7D77F|1.2.0|10.11.6|ELOSNOC*AIDEM*EVIRDREVO'
++ iconv -f ASCII -t UTF-16LE
++ openssl dgst -binary -sha1
++ base64
+ Hash=/tHikPT0xw1zmVW9B3KunnyXxEA=
+ printf 'Using Hash=%s\n' /tHikPT0xw1zmVW9B3KunnyXxEA=
Using Hash=/tHikPT0xw1zmVW9B3KunnyXxEA=
+ curl -L -A 'OverDrive Media Console' --compressed 'https://ofs.contentreserve.com/MP3LicenseAcquisitionService.svc/e610fc69-54f9-4ce5-aa85-acad00e8cf8c?MediaID=12be9ca6-db3c-4013-a000-fce166918282-425&ClientID=C426F217-3A9C-4C7A-9B1F-A490C0E7D77F&OMC=1.2.0&OS=10.11.6&Hash=/tHikPT0xw1zmVW9B3KunnyXxEA='
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 286 0 286 0 0 729 0 --:--:-- --:--:-- --:--:-- 729
++ cat HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm.license
+ printf 'Using License=%s\n' '<LicenseError xmlns="http://license.overdrive.com/2008/03/LicenseError.xsd" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorCode>1003</ErrorCode><ErrorMessage>The requested license is either invalid or already acquired</ErrorMessage><ErrorDetails i:nil="true"/></LicenseError>'
Using License=<LicenseError xmlns="http://license.overdrive.com/2008/03/LicenseError.xsd" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorCode>1003</ErrorCode><ErrorMessage>The requested license is either invalid or already acquired</ErrorMessage><ErrorDetails i:nil="true"/></LicenseError>
++ xmllint --xpath '//*[local-name()="ClientID"]/text()' HackingGrowthHowTodaysFastestGrowingCompaniesD-630.odm.license
XPath set is empty
+ ClientID=
user@MBP Audiobooks %
```
Hi chbrown, I'm new to all this/terminal, just trying to get some audiobooks for commuting now that we are back to in-office work. I sure I'm screwing something up but when I run the command this is what is outputs in terminal. If you tell me how to send debug I can do that also:
"user"@"users"-MacBook-Air Downloads % cd ~/Downloads
~/.local/bin/overdrive download "name of book"_9781984891747_4348059-2.odm
Generating random ClientID=E16EACF5-156C-400A-8784-646E376A0B52
I/O error : Operation not permitted
I/O error : Operation not permitted
warning: failed to load external entity ""name of book"_9781984891747_4348059-2.odm"
I put " " around things I assume I don't want on here except the last line that does have ""
If I copy your command this is what it spits out, so I think??? I installed it correctly:
"user"@"users"-MacBook-Air Downloads % cd ~/Downloads
~/.local/bin/overdrive download Novel.odm
Specified media file does not exist: Novel.odm
again really sorry for what I assume is 100% user error because I'm old and this is new to me. thanks for any help.
i'm on linux and trying this out. i got some odm files from the library, and tried to download the mp3. it seems i'm getting an empty .metadata file. the error is:
.odm.metadata:1: parser error : Document is empty
i see a .metadata file, but it's indeed empty. any ideas or helps on what to try?
I’ve successfully run this great script on macOS, and I’m attempting to run in an iOS environment via Alpine Linux.
I’ve been able to install all of the requisite packages via apk add, and while the script runs, I get the following error when trying to run the download command: “ line 264: /dev/fd/63: No such file or directory”
When running in verbose mode, the error seems to occur as each of the parts are being iterated for download; that is, the directory is successfully made, the part file names are identified, but the download process does not start, nor does it throw a download fail error. Any thoughts?
Hi there,
It appears that overdrive has changed the the license API. I'm getting this for the contents of my .odm.license.
<LicenseError xmlns="http://license.overdrive.com/2008/03/LicenseError.xsd" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorCode>1001</ErrorCode><ErrorMessage>Missing Query Parameter</ErrorMessage><ErrorDetails i:nil="true"/></LicenseError>
Cheers, Jon
only contain one file, and size is very small:
3.6K 9 14 23:16 Part01.mp3
Hi,
I am not a programmer, so it could be that this is basic knowledge. Hope you can help me.
I love using your Overdrive script to get my books, so I can listen to them on other devices. I have it working with just one little issue. Every time I use it, I have to enter the commands you mention in the text below before I can do cd Downloads ---> Overdrive Download MyBook.odm.
Is there any way to make these commands permanent, so I don't have to enter them every time I restart Terminal?
Thanks in advance and thanks for your script!
Chris
At this point, if calling overdrive produces the error message -bash: overdrive: command not found, you'll need to add ~/.local/bin to your PATH. One way to do this:
It seems like OverDrive is moving away from allowing access ODM files. One particular library I use no longer has an option to download them from their site.
Is there a way to get access to the ODM data from the audiobook ID? e.g. XXXX.listen.overdrive.com
?
Try to find an alternative to tidy
.
It's only used in one place, in the extract_metadata
function, to sanitize the metadata XML.
macOS comes with an old version (2006-10-31) of tidy
:
/usr/bin/tidy -v
HTML Tidy for Mac OS X released on 31 October 2006 - Apple Inc. build 16.4
so for the most part it does work, but tidy
is finicky and opinionated and not exactly stable.
There are two types of epubs available for download. The least common type is the "Open EPUB", but it should be easy to implement, since it's a direct download with no DRM.
The second type downloads an ACSM file which you have to open with Adobe Digital Editions to download the encrypted EPUB. Debugging the network traffic, it looks to be relatively straightforward. ADE makes a post request using the info in the ACSM file, and gets another XML file in return, which contains the download link. I might be missing something, as I found a project that makes it sound a bit more complicated: http://indefero.soutade.fr/p/libgourou/ .
Once the encrypted epub is downloaded, the user can use DeDRM to strip the DRM.
Hello! When I try to starting the installation process using the
curl https://chbrown.github.io/overdrive/overdrive.sh > /usr/local/bin/overdrive
chmod +x /usr/local/bin/overdrive
in my Mac terminal, it tells me that permission is denied. How can I fix this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.