From 06dff2110724787287e050299ebd5f82fd5f5a6d Mon Sep 17 00:00:00 2001 From: grepmew <74893644+grepmew@users.noreply.github.com> Date: Sat, 5 Dec 2020 21:24:50 -0800 Subject: [PATCH] Add support for video and pictures when adding or updating a note. (#207) - Sound and video share same [sound:..] tag. See https://github.com/ankitects/anki/blob/0c08ff53179ac1d242b845457db4e4414aef2662/pylib/anki/sound.py#L33 - Tested and verified picture is added and video is added when `addNote` API. --- actions/notes.md | 42 +++++++++++++++++++++++++++++++++++++----- plugin/__init__.py | 45 ++++++++++++++++++++++++++++++--------------- plugin/util.py | 7 ++++++- 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/actions/notes.md b/actions/notes.md index f6e8024..639e972 100644 --- a/actions/notes.md +++ b/actions/notes.md @@ -5,11 +5,11 @@ 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. - AnkiConnect can download audio files and embed them in newly created notes. The corresponding `audio` note member is - optional and can be omitted. If you choose to include it, it should contain a single object or an array of objects + 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 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 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. Normally duplicate cards can not be added and trigger exception. @@ -51,6 +51,22 @@ "fields": [ "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": [ "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** - Modify the fields of an exist note. You can also include audio 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. + 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`, `video`, or `picture` property. Please see the documentation for `addNote` for an explanation of objects in the `audio`, `video`, or `picture` array. *Sample request*: ```json diff --git a/plugin/__init__.py b/plugin/__init__.py index 80f7af0..5fa9030 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -509,7 +509,13 @@ class AnkiConnect: ankiNote = self.createNote(note) 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() self.startEditing() @@ -522,20 +528,20 @@ class AnkiConnect: return ankiNote.id - def addAudio(self, ankiNote, audioObjectOrList): - if audioObjectOrList is None: + def addMedia(self, ankiNote, mediaObjectOrList, mediaType): + if mediaObjectOrList is None: return - if isinstance(audioObjectOrList, list): - audioList = audioObjectOrList + if isinstance(mediaObjectOrList, list): + mediaList = mediaObjectOrList else: - audioList = [audioObjectOrList] + mediaList = [mediaObjectOrList] - for audio in audioList: - if audio is not None and len(audio['fields']) > 0: + for media in mediaList: + if media is not None and len(media['fields']) > 0: try: - data = util.download(audio['url']) - skipHash = audio.get('skipHash') + data = util.download(media['url']) + skipHash = media.get('skipHash') if skipHash is None: skip = False else: @@ -544,14 +550,17 @@ class AnkiConnect: skip = skipHash == m.hexdigest() if not skip: - audioFilename = self.media().writeData(audio['filename'], data) - for field in audio['fields']: + mediaFilename = self.media().writeData(media['filename'], data) + for field in media['fields']: if field in ankiNote: - ankiNote[field] += u'[sound:{}]'.format(audioFilename) + if mediaType is util.MediaType.Picture: + ankiNote[field] += u'

'.format(mediaFilename) + elif mediaType is util.MediaType.Audio or mediaType is util.MediaType.Video: + ankiNote[field] += u'[sound:{}]'.format(mediaFilename) except Exception as e: errorMessage = str(e).replace('&', '&').replace('<', '<').replace('>', '>') - for field in audio['fields']: + for field in media['fields']: if field in ankiNote: ankiNote[field] += errorMessage @@ -575,7 +584,13 @@ class AnkiConnect: ankiNote[name] = value 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() diff --git a/plugin/util.py b/plugin/util.py index a39e206..2b10702 100644 --- a/plugin/util.py +++ b/plugin/util.py @@ -18,12 +18,17 @@ import os import anki import anki.sync import aqt - +import enum # # Utilities # +class MediaType (enum.Enum): + Audio = 1 + Video = 2 + Picture = 3 + def download(url): client = anki.sync.AnkiRequestsClient() client.timeout = setting('webTimeout') / 1000