Add support for video and pictures when adding or updating a note. (#207)

- Sound and video share same [sound:..] tag. See 0c08ff5317/pylib/anki/sound.py (L33)
- Tested and verified picture is added and video is added when `addNote` API.
This commit is contained in:
grepmew 2020-12-05 21:24:50 -08:00 committed by GitHub
parent b03414a8d3
commit 06dff21107
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 21 deletions

View File

@ -5,11 +5,11 @@
Creates a note using the given deck and model, with the provided field values and tags. Returns the identifier of Creates a note using the given deck and model, with the provided field values and tags. Returns the identifier of
the created note created on success, and `null` on failure. the created note created on success, and `null` on failure.
AnkiConnect can download audio files and embed them in newly created notes. The corresponding `audio` note member is AnkiConnect can download audio, video, and picture files and embed them in newly created notes. The corresponding `audio`, `video`, and `picture` note members are
optional and can be omitted. If you choose to include it, it should contain a single object or an array of objects optional and can be omitted. If you choose to include any of them, they should contain a single object or an array of objects
with mandatory `url` and `filename` fields. The `skipHash` field can be optionally provided to skip the inclusion of with mandatory `url` and `filename` fields. The `skipHash` field can be optionally provided to skip the inclusion of
downloaded files with an MD5 hash that matches the provided value. This is useful for avoiding the saving of error downloaded files with an MD5 hash that matches the provided value. This is useful for avoiding the saving of error
pages and stub files. The `fields` member is a list of fields that should play audio when the card is displayed in pages and stub files. The `fields` member is a list of fields that should play audio or video, or show a picture when the card is displayed in
Anki. The `allowDuplicate` member inside `options` group can be set to true to enable adding duplicate cards. Anki. The `allowDuplicate` member inside `options` group can be set to true to enable adding duplicate cards.
Normally duplicate cards can not be added and trigger exception. Normally duplicate cards can not be added and trigger exception.
@ -51,6 +51,22 @@
"fields": [ "fields": [
"Front" "Front"
] ]
}],
"video": [{
"url": "https://cdn.videvo.net/videvo_files/video/free/2015-06/small_watermarked/Contador_Glam_preview.mp4",
"filename": "countdown.mp4",
"skipHash": "4117e8aab0d37534d9c8eac362388bbe",
"fields": [
"Back"
]
}],
"picture": [{
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/A_black_cat_named_Tilly.jpg/220px-A_black_cat_named_Tilly.jpg",
"filename": "black_cat.jpg",
"skipHash": "8d6e4646dfae812bf39651b59d7429ce",
"fields": [
"Back"
]
}] }]
} }
} }
@ -95,6 +111,22 @@
"fields": [ "fields": [
"Front" "Front"
] ]
}],
"video": [{
"url": "https://cdn.videvo.net/videvo_files/video/free/2015-06/small_watermarked/Contador_Glam_preview.mp4",
"filename": "countdown.mp4",
"skipHash": "4117e8aab0d37534d9c8eac362388bbe",
"fields": [
"Back"
]
}],
"picture": [{
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/A_black_cat_named_Tilly.jpg/220px-A_black_cat_named_Tilly.jpg",
"filename": "black_cat.jpg",
"skipHash": "8d6e4646dfae812bf39651b59d7429ce",
"fields": [
"Back"
]
}] }]
} }
] ]
@ -148,8 +180,8 @@
* **updateNoteFields** * **updateNoteFields**
Modify the fields of an exist note. You can also include audio files which will be added to the note with an Modify the fields of an exist note. You can also include audio, video, or picture files which will be added to the note with an
optional `audio` property. Please see the documentation for `addNote` for an explanation of objects in the `audio` array. optional `audio`, `video`, or `picture` property. Please see the documentation for `addNote` for an explanation of objects in the `audio`, `video`, or `picture` array.
*Sample request*: *Sample request*:
```json ```json

View File

@ -509,7 +509,13 @@ class AnkiConnect:
ankiNote = self.createNote(note) ankiNote = self.createNote(note)
audioObjectOrList = note.get('audio') audioObjectOrList = note.get('audio')
self.addAudio(ankiNote, audioObjectOrList) self.addMedia(ankiNote, audioObjectOrList, util.MediaType.Audio)
videoObjectOrList = note.get('video')
self.addMedia(ankiNote, videoObjectOrList, util.MediaType.Video)
pictureObjectOrList = note.get('picture')
self.addMedia(ankiNote, pictureObjectOrList, util.MediaType.Picture)
collection = self.collection() collection = self.collection()
self.startEditing() self.startEditing()
@ -522,20 +528,20 @@ class AnkiConnect:
return ankiNote.id return ankiNote.id
def addAudio(self, ankiNote, audioObjectOrList): def addMedia(self, ankiNote, mediaObjectOrList, mediaType):
if audioObjectOrList is None: if mediaObjectOrList is None:
return return
if isinstance(audioObjectOrList, list): if isinstance(mediaObjectOrList, list):
audioList = audioObjectOrList mediaList = mediaObjectOrList
else: else:
audioList = [audioObjectOrList] mediaList = [mediaObjectOrList]
for audio in audioList: for media in mediaList:
if audio is not None and len(audio['fields']) > 0: if media is not None and len(media['fields']) > 0:
try: try:
data = util.download(audio['url']) data = util.download(media['url'])
skipHash = audio.get('skipHash') skipHash = media.get('skipHash')
if skipHash is None: if skipHash is None:
skip = False skip = False
else: else:
@ -544,14 +550,17 @@ class AnkiConnect:
skip = skipHash == m.hexdigest() skip = skipHash == m.hexdigest()
if not skip: if not skip:
audioFilename = self.media().writeData(audio['filename'], data) mediaFilename = self.media().writeData(media['filename'], data)
for field in audio['fields']: for field in media['fields']:
if field in ankiNote: if field in ankiNote:
ankiNote[field] += u'[sound:{}]'.format(audioFilename) if mediaType is util.MediaType.Picture:
ankiNote[field] += u'<div><img src="{}"><br></div>'.format(mediaFilename)
elif mediaType is util.MediaType.Audio or mediaType is util.MediaType.Video:
ankiNote[field] += u'[sound:{}]'.format(mediaFilename)
except Exception as e: except Exception as e:
errorMessage = str(e).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;') errorMessage = str(e).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
for field in audio['fields']: for field in media['fields']:
if field in ankiNote: if field in ankiNote:
ankiNote[field] += errorMessage ankiNote[field] += errorMessage
@ -575,7 +584,13 @@ class AnkiConnect:
ankiNote[name] = value ankiNote[name] = value
audioObjectOrList = note.get('audio') audioObjectOrList = note.get('audio')
self.addAudio(ankiNote, audioObjectOrList) self.addMedia(ankiNote, audioObjectOrList, util.MediaType.Audio)
videoObjectOrList = note.get('video')
self.addMedia(ankiNote, videoObjectOrList, util.MediaType.Video)
pictureObjectOrList = note.get('picture')
self.addMedia(ankiNote, pictureObjectOrList, util.MediaType.Picture)
ankiNote.flush() ankiNote.flush()

View File

@ -18,12 +18,17 @@ import os
import anki import anki
import anki.sync import anki.sync
import aqt import aqt
import enum
# #
# Utilities # Utilities
# #
class MediaType (enum.Enum):
Audio = 1
Video = 2
Picture = 3
def download(url): def download(url):
client = anki.sync.AnkiRequestsClient() client = anki.sync.AnkiRequestsClient()
client.timeout = setting('webTimeout') / 1000 client.timeout = setting('webTimeout') / 1000