def get_mp4_coverart(path): with open(path, 'rb') as f: data = f.read() cover = MP4Cover(data) return cover
def _save(self, filename, metadata): log.debug("Saving file %r", filename) file = MP4(encode_filename(self.filename)) if file.tags is None: file.add_tags() tags = file.tags if config.setting["clear_existing_tags"]: tags.clear() for name, values in metadata.rawitems(): if name.startswith('lyrics:'): name = 'lyrics' if name in self.__r_text_tags: tags[self.__r_text_tags[name]] = values elif name in self.__r_bool_tags: tags[self.__r_bool_tags[name]] = (values[0] == '1') elif name in self.__r_int_tags: try: tags[self.__r_int_tags[name]] = [ int(value) for value in values ] except ValueError: pass elif name in self.__r_freeform_tags: values = [v.encode("utf-8") for v in values] tags[self.__r_freeform_tags[name]] = values elif name in self.__r_freeform_tags_ci: values = [v.encode("utf-8") for v in values] delall_ci(tags, self.__r_freeform_tags_ci[name]) if name in self.__casemap: name = self.__casemap[name] else: name = self.__r_freeform_tags_ci[name] tags[name] = values elif name == "musicip_fingerprint": tags["----:com.apple.iTunes:fingerprint"] = [ b"MusicMagic Fingerprint%s" % v.encode('ascii') for v in values ] if "tracknumber" in metadata: if "totaltracks" in metadata: tags["trkn"] = [(int(metadata["tracknumber"]), int(metadata["totaltracks"]))] else: tags["trkn"] = [(int(metadata["tracknumber"]), 0)] if "discnumber" in metadata: if "totaldiscs" in metadata: tags["disk"] = [(int(metadata["discnumber"]), int(metadata["totaldiscs"]))] else: tags["disk"] = [(int(metadata["discnumber"]), 0)] covr = [] for image in metadata.images.to_be_saved_to_tags(): if image.mimetype == "image/jpeg": covr.append(MP4Cover(image.data, MP4Cover.FORMAT_JPEG)) elif image.mimetype == "image/png": covr.append(MP4Cover(image.data, MP4Cover.FORMAT_PNG)) if covr: tags["covr"] = covr self._remove_deleted_tags(metadata, tags) file.save()
def set_M4A_data(SONG_INFO, is_quiet, song_path, choice): """ Set the tags in the m4a file passed. """ cover_added = False try: # If more than one choice then call getChoice option = 0 if len(SONG_INFO) > 1: if not is_quiet: option = getChoice(SONG_INFO, 'metadata') elif choice is not None and choice in range(1, len(SONG_INFO)): option = choice SONG_PATH = os.path.join(defaults.DEFAULT.SONG_TEMP_DIR, song_path) audio = MP4(SONG_PATH) # Download the cover image, if failed, pass if dwCover(SONG_INFO, option): imagedata = open(defaults.DEFAULT.COVER_IMG, 'rb').read() audio["covr"] = [ MP4Cover(imagedata, imageformat=MP4Cover.FORMAT_JPEG) ] # REmove the image os.remove(defaults.DEFAULT.COVER_IMG) cover_added = True # If tags are not present then add them try: audio.add_tags() except Exception: pass audio.save() option = int(option) # Add the meta data, the key's can be found at # https://mutagen.readthedocs.io/en/latest/api/mp4.html#mutagen.mp4.MP4Tags audio["\xa9nam"] = SONG_INFO[option].track_name audio["\xa9alb"] = SONG_INFO[option].collection_name audio["\xa9ART"] = SONG_INFO[option].artist_name audio["\xa9day"] = SONG_INFO[option].release_date audio["\xa9gen"] = SONG_INFO[option].primary_genre_name # Adding track number would probably thwor some kind # of render error, will leave for later audio.save() defaults.DEFAULT.SONG_NAME_TO_SAVE = SONG_INFO[ option].track_name + '.m4a' # Rename the downloaded file os.rename( SONG_PATH, os.path.join(defaults.DEFAULT.SONG_TEMP_DIR, defaults.DEFAULT.SONG_NAME_TO_SAVE)) return option, cover_added except Exception as e: return e
def writeTags(self, mp4Path, artwork=True, thumbnail=False): self.log.info("Tagging file: %s." % mp4Path) ext = os.path.splitext(mp4Path)[1][1:] if ext not in valid_output_extensions: self.log.error("File is not the correct format.") sys.exit() video = MP4(mp4Path) try: video.delete() video.save() except IOError: self.log.debug( "Unable to clear original tags, attempting to proceed.") video["\xa9nam"] = self.title # Movie title video["desc"] = self.shortdescription # Short description video["ldes"] = self.description # Long description video["\xa9day"] = self.date # Year video["stik"] = [9] # Movie iTunes category if self.HD is not None: video["hdvd"] = self.HD if self.genre is not None: genre = None for g in self.genre: if genre is None: genre = g['name'] break # else: # genre += ", " + g['name'] video["\xa9gen"] = genre # Genre(s) video[ "----:com.apple.iTunes:iTunMOVI"] = self.xml # XML - see xmlTags method rating = self.rating() if rating is not None: video["----:com.apple.iTunes:iTunEXTC"] = rating if artwork: path = self.getArtwork(mp4Path) if path is not None: cover = open(path, 'rb').read() if path.endswith('png'): video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG) ] # png poster else: video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_JPEG) ] # jpeg poster if self.original: video["\xa9too"] = "MDH:" + os.path.basename(self.original) else: video["\xa9too"] = "MDH:" + os.path.basename(mp4Path) for i in range(3): try: self.log.info("Trying to write tags.") video.save() self.log.info("Tags written successfully.") break except IOError as e: self.log.info("Exception: %s" % e) self.log.exception( "There was a problem writing the tags. Retrying.") time.sleep(5)
def convert_disc(inputfiles): first = min(inputfiles, key=int) last = max(inputfiles, key=int) print(first, last) offset = 150 tracks = [] for i in range(first, last + 1): with audioread.audio_open(str(inputfiles[i])) as f: t_sectors = int(f.duration * 75) tracks.append(Track(i, offset, t_sectors)) offset += t_sectors disc_id = cddb_disc_info(tracks) disc_id[0] = format(disc_id[0], "x") disc_id = [str(x) for x in disc_id] query_cmd = "cddb query {}".format(" ".join(disc_id)) query = requests.get(SERVER_URL, params={ "cmd": query_cmd }) print(query_cmd) if query.status_code != 200: return print("Error") query_lines = query.text.splitlines() query_status = int(query_lines[0].split(" ")[0]) disc_category = "" if query_status == 200: category, match_id, dtitle = query_lines[0].split(" ", 3)[1:] question = { "type": "confirm", "name": "match", "message": "Found match: {}. Is this correct?".format(dtitle) } answer = prompt(question) if not answer["match"]: disc_category = "OTHER-NOT-LISTED" else: disc_category = category elif query_status == 210 or query_status == 211: question = { "type": "list", "name": "match", "message": "Multiple matches found, please select one", "choices": [ { "name": "Other (not listed)", "value": "OTHER-NOT-LISTED" } ] } for i, match in enumerate(query_lines[1:-1]): category, match_id, dtitle = match.split(" ", 2) question["choices"].append({ "name": dtitle, "value": category }) answer = prompt(question) disc_category = answer["match"] if disc_category == "OTHER-NOT-LISTED" or query_status == 202: print("Could not find match in VGMDB CDDB.") question = { "type": "input", "name": "link", "message": "Please paste VGMDB ID, or leave blank to cancel:" } answer = prompt(question) vgmdb_link = answer["link"] disc_category = None if disc_category is None: vgmdb = requests.get("https://vgmdb.info/album/{}".format(vgmdb_link), params={ "format": "json" }) if vgmdb.status_code != 200: return print("Error") info = vgmdb.json() album = info["names"]["en"] if " / " in album: artist, album = info["names"]["en"].split(" / ", 1) else: artist = info["arrangers"][0]["names"]["en"] if " / " in album: album, artist = album.split(" / ", 1) art_get = requests.get(info["picture_full"]) if len(info["covers"]) > 0: try: front_cover = next(filter(lambda c: c["name"] == "Front", info["covers"])) art_image = requests.get(front_cover["full"]) except: art_image = requests.get(info["picture_full"]) else: art_image = requests.get(info["picture_full"]) if art_image.status_code == 200: cover = art_image.content if len(info["discs"]) > 1: disc_number = int(input("Disc number:")) - 1 sdn = "y" in input("Save disc number?") else: disc_number = 0 sdn = False tracks = info["discs"][disc_number]["tracks"] for i, track in enumerate(range(first, last + 1)): track_name = tracks[i]["names"]["Japanese"] try: track_name = tracks[i]["names"]["Romaji"] except KeyError: pass try: track_name = tracks[i]["names"]["English"] except KeyError: pass converted_filename = "output/{} – {}.m4a".format(track, track_name.replace("/", "")) convert_cmd = [ "ffmpeg", "-y", "-i", inputfiles[track], "-c:a", "alac", "-c:v", "copy", converted_filename ] run(convert_cmd) audio = MP4(converted_filename) try: audio.delete(filename=converted_filename) audio.add_tags() except: pass audio.tags[TAGS["title"]] = track_name audio.tags[TAGS["artist"]] = artist audio.tags[TAGS["album"]] = album audio.tags[TAGS["genre"]] = "Anime" audio.tags[TAGS["year"]] = info["release_date"].split("-", 1)[0] audio.tags[TAGS["track"]] = ((track, last + 1 - first),) if sdn: audio.tags[TAGS["disc"]] = ((disc_number+1, len(info["discs"])),) if cover is not None: audio.tags[TAGS["cover"]] = (MP4Cover(cover, imageformat=MP4Cover.FORMAT_JPEG),) audio.save() else: read_cmd = "cddb read {} {}".format(disc_category, disc_id[0]) read = requests.get(SERVER_URL, params={ "cmd": read_cmd }) if query.status_code != 200: return print("Error") read_lines = read.text.splitlines() read_status = int(read_lines[0].split(" ")[0]) if read_status == 210: data = {} for line in read_lines[1:-1]: if line.startswith("#"): continue k, v = line.strip().split('=') data[k] = v artist, album = data["DTITLE"].split(" / ", 1) catalog, album = album.split(" ", 1) catalog = catalog[1:-1] if " / " in album: album, artist = album.split(" / ", 1) art_search = requests.get("https://vgmdb.info/search/albums", params={ "format": "json", "q": catalog }) cover = None if art_search.status_code == 200: results = art_search.json()["results"]["albums"] if len(results) > 0: art_get = requests.get("https://vgmdb.info/{}".format(results[0]["link"])) if art_get.status_code == 200: try: results_json = art_get.json() if len(results_json["covers"]) > 0: try: front_cover = next(filter(lambda c: c["name"] == "Front", results_json["covers"])) art_image = requests.get(front_cover["full"]) except: art_image = requests.get(info["picture_full"]) else: art_image = requests.get(results_json["picture_full"]) if art_image.status_code == 200: cover = art_image.content except: pass for i, track in enumerate(range(first, last + 1)): converted_filename = "output/{} – {}.m4a".format(track, data["TTITLE{}".format(i)].replace("/", "")) convert_cmd = [ "ffmpeg", "-y", "-i", inputfiles[track], "-c:a", "alac", "-c:v", "copy", converted_filename ] run(convert_cmd) audio = MP4(converted_filename) try: audio.delete(filename=converted_filename) audio.add_tags() except: pass audio.tags[TAGS["title"]] = data["TTITLE{}".format(i)] audio.tags[TAGS["artist"]] = artist audio.tags[TAGS["album"]] = album audio.tags[TAGS["genre"]] = "Anime" audio.tags[TAGS["year"]] = data["DYEAR"] audio.tags[TAGS["track"]] = ((track, last + 1 - first),) if cover is not None: audio.tags[TAGS["cover"]] = (MP4Cover(cover, imageformat=MP4Cover.FORMAT_JPEG),) audio.save()
def writeTags(self, mp4Path, artwork=True, thumbnail=False): self.log.info("Tagging file: %s." % mp4Path) ext = os.path.splitext(mp4Path)[1][1:] if ext not in valid_tagging_extensions: self.log.error("File is not the correct format.") return False video = MP4(mp4Path) checktags = {} checktags["tvsh"] = self.show # TV show title checktags["\xa9nam"] = self.title # Video title checktags["tven"] = self.title # Episode title checktags["desc"] = self.shortDescription() # Short description checktags[ "ldes"] = self.description # Long description (same a short for tv) network = [x['name'] for x in self.network] checktags["tvnn"] = network # Network if self.airdate != "0000-00-00": checktags["\xa9day"] = self.airdate # Airdate checktags["tvsn"] = [self.season] # Season number checktags["disk"] = [(int(self.season), 0)], checktags["disk"] = [(int(self.season), 0)] # Season number as disk checktags["\xa9alb"] = self.show + ", Season " + str( self.season) # iTunes Album as Season checktags["tves"] = [self.episode] # Episode number checktags["trkn"] = [(int(self.episode), len(self.seasondata)) ] # Episode number iTunes #checktags["stik"] = [10] # TV show iTunes category #if self.HD is not None: # checktags["hdvd"] = self.HD if self.genre is not None: genre = None for g in self.genre: if genre is None: genre = g['name'] break checktags["\xa9gen"] = genre # video["\xa9gen"] = self.genre.replace('|', ',')[1:-1] # Genre(s) #checktags["----:com.apple.iTunes:iTunMOVI"] = self.xml.encode("UTF-8", errors="ignore") # XML - see xmlTags method # video["----:com.apple.iTunes:iTunEXTC"] = self.setRating() # iTunes content rating if artwork: path = self.getArtwork(mp4Path, thumbnail=thumbnail) if path is not None: cover = open(path, 'rb').read() if path.endswith('png'): checktags["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG) ] # png poster else: checktags["covr"] = [ MP4Cover(cover, MP4Cover.FORMAT_JPEG) ] # jpeg poster ProcessTags = False for keys, values in checktags.items(): if video.tags == None: ProcessTags = True break elif keys in video.tags: if video.tags[keys] != [values]: self.log.debug(keys + " tag does not match and will be updated") ProcessTags = True break else: self.log.debug(keys + " will be added") ProcessTags = True if not ProcessTags: self.log.info("All MP4 tags match the original, skipping tagging.") else: try: video.delete() except IOError: self.log.debug( "Unable to clear original tags, attempting to proceed.") video["tvsh"] = self.show # TV show title video["\xa9nam"] = self.title # Video title video["tven"] = self.title # Episode title video["desc"] = self.shortDescription() # Short description video[ "ldes"] = self.description # Long description (same a short for tv) network = [x['name'] for x in self.network] video["tvnn"] = network # Network if self.airdate != "0000-00-00": video["\xa9day"] = self.airdate # Airdate video["tvsn"] = [self.season] # Season number video["disk"] = [(int(self.season), 0)] # Season number as disk video["\xa9alb"] = self.show + ", Season " + str( self.season) # iTunes Album as Season video["tves"] = [self.episode] # Episode number video["trkn"] = [(int(self.episode), len(self.seasondata)) ] # Episode number iTunes #video["stik"] = [10] # TV show iTunes category #if self.HD is not None: # video["hdvd"] = self.HD if self.genre is not None: genre = None for g in self.genre: if genre is None: genre = g['name'] break video["\xa9gen"] = genre # video["\xa9gen"] = self.genre.replace('|', ',')[1:-1] # Genre(s) #video["----:com.apple.iTunes:iTunMOVI"] = self.xml.encode("UTF-8", errors="ignore") # XML - see xmlTags method # video["----:com.apple.iTunes:iTunEXTC"] = self.setRating() # iTunes content rating if artwork: path = self.getArtwork(mp4Path, thumbnail=thumbnail) if path is not None: cover = open(path, 'rb').read() if path.endswith('png'): video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG) ] # png poster else: video["covr"] = [ MP4Cover(cover, MP4Cover.FORMAT_JPEG) ] # jpeg poster if self.original: video["\xa9too"] = "MDH:" + os.path.basename(self.original) else: video["\xa9too"] = "MDH:" + os.path.basename(mp4Path) MP4(mp4Path).delete(mp4Path) for i in range(3): try: self.log.info("Trying to write tags.") video.save() self.log.info("Tags written successfully.") return True except IOError as e: self.log.exception( "There was a problem writing the tags. Retrying.") time.sleep(5) return False
def TagTrack(self, folder, export=''): ''' 'Tag' a song's trackdata with the infomation given in folder Call once all download tasks is finished Suppports MP3,M4A(ALAC/MP4),FLAC ''' export = export if export else self.output folder = str(folder) audio = [ f for f in os.listdir(folder) if f.split('.')[-1].lower() in ['mp3', 'm4a', 'flac', 'ogg'] ] if not audio: logger.error( 'Supported audio file for %s is missing,Cannot continue formatting!' % folder) return audio = audio[-1] audio = self.GenerateDownloadPath(filename=audio, folder=folder) format = audio.split('.')[-1].lower() # Locate audio file,uses last match track = self.GenerateDownloadPath(filename='track.json', folder=folder) # Locate track file cover = self.GenerateDownloadPath(filename='cover.jpg', folder=folder) # Locate cover image track = json.loads(open(track, encoding='utf-8').read()) tHelper = TrackHelper(track) def write_keys(song): # Write trackdatas song['title'] = tHelper.TrackName song[ 'artist'] = tHelper.Artists if tHelper.Artists else 'Various Artists' song[ 'album'] = tHelper.AlbumName if tHelper.AlbumName else 'Unknown' song['tracknumber'] = str(tHelper.TrackNumber) song['date'] = str(tHelper.TrackPublishTime) song.save() if format == 'm4a': # Process m4a files: Apple’s MP4 (aka M4A, M4B, M4P) song = easymp4.EasyMP4(audio) write_keys(song) if os.path.exists(cover): song = MP4(audio) # Write cover image song['covr'] = [MP4Cover(open(cover, 'rb').read())] song.save() elif format == 'mp3': # Process mp3 files: MPEG audio stream information and tags song = EasyMP3(audio) write_keys(song) if os.path.exists(cover): song = ID3(audio) # Write cover image song.add( APIC(encoding=3, mime='image/jpeg', type=3, desc='Cover', data=open(cover, 'rb').read())) song.save() elif format == 'flac': # Process FLAC files:Free Lossless Audio Codec song = FLAC(audio) write_keys(song) if os.path.exists(cover): pic = Picture() pic.data = open(cover, 'rb').read() pic.mime = 'image/jpeg' song.add_picture(pic) song.save() elif format == 'ogg': # Process OGG files:Ogg Encapsulation Format song = OggVorbis(audio) write_keys(song) if os.path.exists(cover): pic = Picture() pic.data = open(cover, 'rb').read() pic.mime = 'image/jpeg' song["metadata_block_picture"] = [ base64.b64encode(pic.write()).decode('ascii') ] song.save() # Rename & move file savename = tHelper.SanitizedTitle + '.' + format try: path = self.GenerateDownloadPath(filename=savename, folder=export) shutil.copy(audio, path) logger.debug('Exported audio to path %s' % path[-truncate_length:]) return path except Exception as e: raise Exception('While copying audio file:%s' % e)
def FormatSong(self, folder, export=''): ''' 'Format' a song's metadata with the infomation given in folder Call once all download tasks is finished Suppports MP3,M4A(ALAC/MP4),FLAC ''' export = export if export else self.output folder = str(folder) audio = [ f for f in os.listdir(folder) if f.split('.')[-1] in ['mp3', 'm4a', 'flac'] ] if not audio: return audio = audio[-1] audio = self.GenerateDownloadPath(filename=audio, folder=folder) format = audio.split('.')[-1] # Locate audio file,uses last match meta = self.GenerateDownloadPath(filename='meta.json', folder=folder) # Locate meta file cover = self.GenerateDownloadPath(filename='cover.jpg', folder=folder) # Locate cover image if not audio: self.log('audio.m4a / audio.mp3 / audio.flac', format=strings.ERROR_FILE_MISSING) return if not os.path.exists(meta): self.log('meta.json', format=strings.ERROR_FILE_MISSING) return if not os.path.exists(cover): self.log('cover.jpg', format=strings.WARN_FILE_MISSING) meta = json.loads(open(meta, encoding='utf-8').read()) def write_keys(song): # Write metadatas song['title'] = meta['title'] song['artist'] = meta['author'] song['album'] = meta['album'] song.save() if format == 'm4a': # Process m4a files: Apple’s MP4 (aka M4A, M4B, M4P) song = easymp4.EasyMP4(audio) write_keys(song) if os.path.exists(cover): song = MP4(audio) # Write cover image song['covr'] = [MP4Cover(open(cover, 'rb').read())] song.save() elif format == 'mp3': # Process mp3 files: MPEG audio stream information and tags song = EasyMP3(audio) write_keys(song) if os.path.exists(cover): song = ID3(audio) # Write cover image song.add( APIC(encoding=3, mime='image/jpeg', type=3, desc='Cover', data=open(cover, 'rb').read())) song.save() elif format == 'flac': # Process FLAC files:Free Lossless Audio Codec song = FLAC(audio) write_keys(song) if os.path.exists(cover): pic = Picture() pic.data = open(cover, 'rb').read() pic.mime = 'image/jpeg' song.add_picture(pic) song.save() # Rename & move file savename = '{1} - {0}.{2}'.format(meta['title'], meta['author'], format) try: path = self.GenerateDownloadPath(filename=savename, folder=export) shutil.copy(audio, path) self.log(path, format=strings.INFO_EXPORT_COMPLETE) except Exception as e: self.log(e, format=strings.ERROR_INVALID_OPERATION) return path
song_path = 'C:/Users/Lomzem/Desktop/newest-songs/' abspath_song = lambda file: song_path + file abspath_art = lambda file: artwork_path + file def get_pairs(): pair_list = [] for song_file in os.listdir(song_path): song_file_name, song_file_ext = os.path.splitext(song_file) for art_file in os.listdir(artwork_path): art_file_name, art_file_ext = os.path.splitext(art_file) if song_file_name == art_file_name: complete_pair = (abspath_song(song_file), abspath_art(art_file)) pair_list.append(complete_pair) return pair_list pair_list = get_pairs() for pair in pair_list: song_file, art_file = pair audio = MP4(song_file) with open(art_file, 'rb') as f: audio['covr'] = [ MP4Cover(f.read()) ] audio.save()
def downloadSong(web_url,baseFolder,file,performance,username): # Construct full path to filename filename = baseFolder + file # If the file already exists, skip it if path.exists(filename): print(f"ALREADY EXISTS - {filename}") return 0 # Print filename print(f"Downloading {filename}") try: # Print out the web_url for debugging purposes # TODO: Convert to debug message? #print(web_url) # The web_url returns an HTML page that contains the link to the content we wish to download with request.urlopen(web_url) as url: # First get the HTML for the web_url htmlstr = str(url.read()) #print(htmlstr) # Next, parse out the actual media_url, which is in the content field of the "twitter:player:stream" object # We need to strip out the "amp;" values and convert the "+" value to URL-friendly value media_url = unquote(re.search('twitter:player:stream.*?content=".*?"',htmlstr).group(0).split('"')[2]).replace("amp;","").replace("+","%2B") #print(media_url) # Print out the media_url for debugging purposes # TODO: Convert this to a debug message? #print(media_url) # Open the file in binary write mode and write the data from the media_url f = open(filename,'w+b') f.write(request.urlopen(media_url).read()) f.close() except Exception as e: print("FAILED TO DOWNLOAD!!!!!!!!!!!!!!") if "'NoneType' object has no attribute 'group'" in str(e): print("Recording not available - attempting to play in order to make it available") media_url = unquote(re.search('twitter:player" content=".*?"',htmlstr).group(0).split('"')[2]).replace("amp;","").replace("+","%2B") # webbrowser registration does not seem to be needed #webbrowser.register('chrome',None,webbrowser.BackgroundBrowser("C://Program Files (x86)//Google//Chrome//Application//chrome.exe")) #webbrowser.get('chrome').open(media_url) webbrowser.open(media_url) #print(f"NEED TO PLAY: {media_url}") else: print(str(e)) print("-----") #if (not "HTTP Error 504" in str(e)) and (not "HTTP Error 410" in str(e)) and (not "'NoneType' object has no attribute 'group'" in str(e)): #raise return 1 try: # Calculate necessary dates createdat = performance["created_at"] perfdate = datetime.strptime(createdat[0:10],"%Y-%m-%d") # Set the album date to the start of the week on which the song was created albumdate = (perfdate - timedelta(days=perfdate.weekday())).strftime("%Y-%m-%d") albumyear = createdat[0:4] # Write the tags for the M4A file af = MP4(filename) af["\xa9nam"] = performance["fixed_title"] af["\xa9ART"] = performance["performers"] # Set Album Artist to the username we searched for af["aART"] = username # Android seems to have a bug where wrong art is displayed if "Album" tag is empty so set it to "Smule" followed by current date af["\xa9alb"] = f"Smule - {albumdate}" af["\xa9day"] = f"{albumyear}" af["purd"] = createdat af["\xa9cmt"] = f"Performed on {createdat}" # Write the JPEG to the M4A file as album cover. Ignore any errors reading the image try: pic_url = performance['display_pic_url'] img = MP4Cover(request.urlopen(pic_url).read(), imageformat=MP4Cover.FORMAT_JPEG) af["covr"] = [img] except: pass # Save the updated tags to the file af.save() except Exception as e: print("FAILED TO UPDATE TAGS!!!") print(str(e)) return 0 # Print pic URL for debugging purposes # print(pic_url) return 0
def tagFile(self, filename, metadata, art_url): if not self.file_done: return image = None if art_url is not None: self.getFile('artwork.jpg', art_url, True) try: with open('artwork.jpg', 'rb') as file: image = file.read() except: pass if filename.endswith('.mp3'): audio = MP3(filename, ID3=ID3) try: audio.add_tags() except: pass if image: audio.tags.add( APIC( encoding=3, mime='image/jpeg', type=3, desc=u'Cover', data=image ) ) audio.tags["TIT2"] = TIT2(encoding=3, text=(metadata.get('title', ''))) try: audio.tags["TPE1"] = TPE1(encoding=3, text=metadata.get('artist', '')) except: pass audio.tags["TDRC"] = TDRC(encoding=3, text=(metadata.get('year', ''))) audio.tags["TCON"] = TCON(encoding=3, text=(metadata.get('genre', ' '))) audio.save() elif filename.endswith('.flac'): audio = FLAC(filename) try: audio.add_tags() except: pass audio.tags['title'] = metadata['title'] audio.tags['artist'] = metadata['artist'] audio.tags['year'] = metadata['year'] audio.tags['genre'] = metadata['genre'] audio.tags.add( APIC( encoding=3, mime='image/jpeg', type=3, desc=u'Cover', data=image ) ) audio.save() elif filename.endswith('.m4a'): audio = MP4(filename) try: audio.add_tags() except: pass covr = [] covr.append(MP4Cover(image, MP4Cover.FORMAT_JPEG)) audio.tags['covr'] = covr audio.tags['title'] = metadata['title'] audio.tags['artist'] = metadata['artist'] #audio.tags['year'] = metadata['year'] audio.tags['genre'] = metadata['genre'] audio.save() if os.path.isfile('artwork.jpg'): os.remove('artwork.jpg')
def start_process(filenames, mode): """ This is the main funtion of the script where it does its main processing.\n filenames is the list of files to be processed\n mode = 1,2,3 or 4\n 1 means mp4 to tagged mp4\n 2 means mp4 with sub to subbed and tagged mp4\n 3 means mkv to tagged mp4\n 4 means mkv with sub to subbed and tagged mp4 """ for filename in filenames: try: title = filename[:-4] stream_md = collect_stream_metadata(filename) streams_to_process = [] dvdsub_exists=False for stream in stream_md['streams']: if not stream['codec_name'] in sub_codec_blacklist: streams_to_process.append(stream['index']) else: dvdsub_exists=True print('\nSearching IMDb for "{}"'.format(title)) imdb = Imdb() movie_results = [] results = imdb.search_for_title(title) for result in results: if result['type'] == "feature": movie_results.append(result) if not movie_results: while not movie_results: title = input('\nNo results for "' + title + '" Enter alternate/correct movie title >> ') results = imdb.search_for_title(title) for result in results: if result['type'] == "feature": movie_results.append(result) # The most prominent result is the first one # mpr - Most Prominent Result mpr = movie_results[0] print('\nFetching data for {} ({})'.format(mpr['title'], mpr['year'])) # imdb_movie is a dict of info about the movie imdb_movie = imdb.get_title(mpr['imdb_id']) imdb_movie_title = imdb_movie['base']['title'] imdb_movie_year = imdb_movie['base']['year'] imdb_movie_id = mpr['imdb_id'] imdb_movie_rating = imdb_movie['ratings']['rating'] if not 'outline' in imdb_movie['plot']: imdb_movie_plot_outline = (imdb_movie['plot']['summaries'][0] ['text']) print("\nPlot outline does not exist. Fetching plot summary " "instead.\n\n") else: imdb_movie_plot_outline = imdb_movie['plot']['outline']['text'] # Composing a string to have the rating and the plot of the # movie which will go into the 'comment' metadata of the # mp4 file. imdb_rating_and_plot = str('IMDb rating [' + str(float(imdb_movie_rating)) + '/10] - ' + imdb_movie_plot_outline) imdb_movie_genres = imdb.get_title_genres(imdb_movie_id)['genres'] # Composing the 'genre' string of the movie. # I use ';' as a delimeter to searate the multiple genre values genre = ';'.join(imdb_movie_genres) newfilename = (imdb_movie_title + ' (' + str(imdb_movie_year) + ').mp4') # We don't want the characters not allowed in a filename newfilename = (newfilename .replace(':', ' -') .replace('/', ' ') .replace('?', '')) command = "" stream_map = [] for f in streams_to_process: stream_map.append("-map 0:{}".format(f)) stream_map_str = ' '.join(stream_map) if mode == 1: # it is required to rename it as its already an mp4 file that # wasn't proccessed by ffmpeg os.rename(filename, newfilename) if mode == 2 or mode == 4: command = ('ffmpeg -i "' + filename + '" -sub_charenc UTF-8 -i "' + filename[:-4] + '.srt" ' + stream_map_str + ' -map 1 -c copy -c:s mov_text ' '"' + newfilename + '"') subprocess.run(shlex.split(command)) if mode == 3: command = ('ffmpeg -i ' + '"' + filename + '" ' + stream_map_str + ' -c copy -c:s mov_text ' '"' + newfilename + '"') subprocess.run(shlex.split(command)) if dvdsub_exists: print("\nRemoved DVD Subtitles due to uncompatibility with " "mp4 file format") # The poster is fetched from tmdb only if there is no file # named " filename + '.jpg' " in the working directory # this way user can provide their own poster image to be used poster_filename = filename[:-4] + '.jpg' if not os.path.isfile(poster_filename): print('\nFetching the movie poster...') tmdb_find = tmdb.Find(imdb_movie_id) tmdb_find.info(external_source = 'imdb_id') path = tmdb_find.movie_results[0]['poster_path'] complete_path = r'https://image.tmdb.org/t/p/w780' + path uo = urllib.request.urlopen(complete_path) with open(poster_filename, "wb") as poster_file: poster_file.write(uo.read()) poster_file.close() video = MP4(newfilename) with open(poster_filename, "rb") as f: video["covr"] = [MP4Cover( f.read(), imageformat=MP4Cover.FORMAT_JPEG)] video['\xa9day'] = str(imdb_movie_year) video['\xa9nam'] = imdb_movie_title video['\xa9cmt'] = imdb_rating_and_plot video['\xa9gen'] = genre print('\nAdding poster and tagging file...') try: video.save() # I have encounterd this error in pevious version # of script, now I handle it by removing the metadata # of the file. That seems to solve the probelem except OverflowError: remove_meta_command = ('ffmpeg -i "' + newfilename + '" -codec copy -map_metadata -1 "' + newfilename[:-4] + 'new.mp4"') subprocess.run(shlex.split(remove_meta_command)) video_new = MP4(newfilename[:-4] + 'new.mp4') with open(poster_filename, "rb") as f: video_new["covr"] = [MP4Cover( f.read(), imageformat=MP4Cover.FORMAT_JPEG)] video_new['\xa9day'] = str(imdb_movie_year) video_new['\xa9nam'] = imdb_movie_title video_new['\xa9cmt'] = imdb_rating_and_plot video_new['\xa9gen'] = genre print('\nAdding poster and tagging file...') try: video_new.save() if not os.path.exists('auto fixed files'): os.makedirs('auto fixed files') os.rename(newfilename[:-4] + 'new.mp4', 'auto fixed files\\' + newfilename[:-4] + '.mp4') os.remove(newfilename) except OverflowError: errored_files.append(filename + (' - Could not save even after' 'striping metadata')) continue os.remove(poster_filename) print('\n' + filename + (' was proccesed successfuly!\n\n====================' '======================================')) except Exception as e: print('\nSome error occured while processing ' + filename + '\n\n====================================================') errored_files.append(filename + ' - ' + str(e)) PrintException()
def writeTags(self, mp4Path, artwork=True, thumbnail=False): self.log.debug("Tagging file %s" % mp4Path) if MkvtoMp4(self.settings).validSource(mp4Path) == True: video = MP4(mp4Path) try: video.delete() except IOError: self.log.debug( "Unable to clear original tags, attempting to proceed") video["\xa9nam"] = self.title # Movie title #video["desc"] = self.shortdescription # Short description #video["ldes"] = self.description # Long description video["\xa9day"] = self.date # Year #video["stik"] = [9] # Movie iTunes category if self.HD is not None: video["hdvd"] = self.HD #if self.genre is not None: # genre = None # for g in self.genre: # if genre is None: # genre = g['name'] # break # # else: # # genre += ", " + g['name'] # video["\xa9gen"] = genre # Genre(s) #video["----:com.apple.iTunes:iTunMOVI"] = self.xml # XML - see xmlTags method #rating = self.rating() #if rating is not None: # video["----:com.apple.iTunes:iTunEXTC"] = rating if artwork: path = self.getArtwork(mp4Path) if path is not None: cover = open(path, 'rb').read() if path.endswith('png'): video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG) ] # png poster else: video["covr"] = [ MP4Cover(cover, MP4Cover.FORMAT_JPEG) ] # jpeg poster #if self.original: # video["\xa9too"] = ("meks-ffmpeg movie [%s-%s]" % (self.provider, self.providerid)) #else: # video["\xa9too"] = ("meks-ffmpeg movie [%s-%s]" % (self.provider, self.providerid)) video = metadata_stamper.stamp_encoder( video=video, save=False, stamp=("movie [%s-%s]" % (self.provider, self.providerid))) for i in range(3): try: self.log.debug("Trying to write tags") video.save() self.log.info("Tags written successfully") return True except IOError as e: self.log.exception( "There was a problem writing the tags. Retrying") time.sleep(5) else: self.log.error("The file is invalid") raise IOError
def writeTags(self, path, artwork=True, thumbnail=False, width=None, height=None): self.log.info("Tagging file: %s." % path) if width and height: try: self.setHD(width, height) except: self.log.exception("Unable to set HD tag.") try: video = MP4(path) except MP4StreamInfoError: self.log.error( 'File is not a valid MP4 file and cannot be tagged.') return False try: video.delete() except: self.log.debug("Unable to clear original tags, will proceed.") if self.mediatype == MediaType.Movie: video["\xa9nam"] = self.title # Movie title video["desc"] = self.tagline # Short description video["ldes"] = self.description # Long description video["\xa9day"] = self.date # Year video["stik"] = [9] # Movie iTunes category elif self.mediatype == MediaType.TV: video["tvsh"] = self.showname # TV show title video["\xa9nam"] = self.title # Video title video["tven"] = self.title # Episode title video["desc"] = self.shortDescription # Short description video["ldes"] = self.description # Long description network = [x['name'] for x in self.network] video["tvnn"] = network # Network video["\xa9day"] = self.airdate # Airdate video["tvsn"] = [self.season] # Season number video["disk"] = [(self.season, 0)] # Season number as disk video["\xa9alb"] = self.showname + ", Season " + str( self.season) # iTunes Album as Season video["tves"] = [self.episode] # Episode number video["trkn"] = [ (self.episode, len(self.seasondata.get('episodes', []))) ] # Episode number iTunes video["stik"] = [10] # TV show iTunes category if self.HD: video["hdvd"] = self.HD if self.genre and len(self.genre) > 0: video["\xa9gen"] = self.genre[0].get('name') video["----:com.apple.iTunes:iTunMOVI"] = self.xml.encode( "UTF-8", errors="ignore") # XML - see xmlTags method if self.rating: video["----:com.apple.iTunes:iTunEXTC"] = self.rating.encode( "UTF-8", errors="ignore") # iTunes content rating if artwork: coverpath = self.getArtwork(path, thumbnail=thumbnail) if coverpath is not None: cover = open(coverpath, 'rb').read() if path.endswith('png'): video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG) ] # png poster else: video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_JPEG) ] # jpeg poster if self.original: video["\xa9too"] = "SMA:" + os.path.basename(self.original) else: video["\xa9too"] = "SMA:" + os.path.basename(path) try: self.log.info("Trying to write tags.") video.save() self.log.info("Tags written successfully.") return True except: self.log.exception("There was an error writing the tags.") return False
def tag(tHelper: TrackHelper, audio: str, cover: str): from mutagen.flac import FLAC, Picture from mutagen.id3 import ID3, APIC from mutagen.mp3 import EasyMP3 from mutagen import easymp4 from mutagen.mp4 import MP4, MP4Cover from mutagen.oggvorbis import OggVorbis def write_keys(song): # Write trackdatas song['title'] = tHelper.TrackName song['artist'] = tHelper.Artists song['album'] = tHelper.AlbumName song['tracknumber'] = str(tHelper.TrackNumber) song['date'] = str(tHelper.TrackPublishTime) song.save() format = audio.split('.')[-1].lower() if format == 'm4a': # Process m4a files: Apple’s MP4 (aka M4A, M4B, M4P) song = easymp4.EasyMP4(audio) write_keys(song) if exists(cover): song = MP4(audio) # Write cover image song['covr'] = [MP4Cover(open(cover, 'rb').read())] song.save() elif format == 'mp3': # Process mp3 files: MPEG audio stream information and tags song = EasyMP3(audio) write_keys(song) if exists(cover): song = ID3(audio) # Write cover image song.add( APIC(encoding=3, mime='image/jpeg', type=3, desc='Cover', data=open(cover, 'rb').read())) song.save() elif format == 'flac': # Process FLAC files:Free Lossless Audio Codec song = FLAC(audio) write_keys(song) if exists(cover): pic = Picture() pic.data = open(cover, 'rb').read() pic.mime = 'image/jpeg' song.add_picture(pic) song.save() elif format == 'ogg': # Process OGG files:Ogg Encapsulation Format song = OggVorbis(audio) write_keys(song) if exists(cover): pic = Picture() pic.data = open(cover, 'rb').read() pic.mime = 'image/jpeg' song["metadata_block_picture"] = [ base64.b64encode(pic.write()).decode('ascii') ] song.save() return True
def as_m4a(self): """ Embed metadata to M4A files. """ # Apple has specific tags - see mutagen docs - # http://mutagen.readthedocs.io/en/latest/api/mp4.html tags = { 'album': '\xa9alb', 'artist': '\xa9ART', 'date': '\xa9day', 'title': '\xa9nam', 'originaldate': 'purd', 'comment': '\xa9cmt', 'group': '\xa9grp', 'writer': '\xa9wrt', 'genre': '\xa9gen', 'tracknumber': 'trkn', 'albumartist': 'aART', 'disknumber': 'disk', 'cpil': 'cpil', 'albumart': 'covr', 'copyright': 'cprt', 'tempo': 'tmpo', 'lyrics': '\xa9lyr', 'publisher': '----:com.apple.iTunes:publisher', #equals to label for NI Traktor 'audiotype': '----:com.apple.iTunes:AUDIOTYPE', #beatport/traktor specific: track/stem/sample 'ISRC': '----:com.apple.iTunes:ISRC', 'PUBLISHEDDATE': '----:com.apple.iTunes:PUBLISHEDDATE', #Beatport release date 'RELEASEDATE': '----:com.apple.iTunes:RELEASEDATE', #Beatport "street" date 'UFID': '----:com.apple.iTunes:UFID', #unique Beatport identifier 'version': '----:com.apple.iTunes:VERSION', #Version of the Song ("Mix") 'remixer': '----:com.apple.iTunes:REMIXER', 'discription': 'desc' } audiofile = MP4(self.music_file) for key in self.metadata.keys(): if self.overwrite == True or not audiofile[tags[key]]: #print(audiofile[tags[key]]) if tags[key].startswith('----:com.apple.iTunes:'): #use MP4FreeForm method tag = self._convert_to_FreeForm_string(self.metadata[key]) elif key == 'albumart': albumart = urllib.request.urlopen(self.metadata[key]) tag = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] albumart.close() else: tag = self.metadata[key] audiofile[tags[key]] = tag elif self.overwrite == 'merge' and audiofile[tags[key]]: if tags[key].startswith('----:com.apple.iTunes:'): #use MP4FreeForm method tag = self._convert_to_FreeForm_string(self.metadata[key]) elif key == 'albumart': albumart = urllib.request.urlopen(self.metadata[key]) tag = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] albumart.close() else: tag = self.metadata[key] if not isinstance(tag, list): tag = [tag] if not isinstance(audiofile[tags[key]], list): audiofile_tag = [audiofile[tags[key]]] audiofile[tags[key]] = list(set().union( audiofile[tags[key]], tag)) audiofile.save() return True
def start_process(filenames, mode): """ This is the main funtion of the script where it does its main processing.\n filenames is the list of files to be processed\n mode = 1,2,3 or 4\n 1 means mp4 to tagged mp4\n 2 means mp4 with sub to subbed and tagged mp4\n 3 means mkv to tagged mp4\n 4 means mkv with sub to subbed and tagged mp4 """ searchindex = 0 for filename in filenames: try: title = filename[:-4] print('\nFetching movie data for "' + title + '"') search = tmdb.Search() response = search.movie(query=title) # getting a Movies object from the id that we got from the search # results try: # sometimes blank search results are returned tmdb_movie = tmdb.Movies(search.results[searchindex]['id']) except IndexError: while len(search.results) == 0: title = input("\nCould not find the movie, Enter" " alternate movie title >> ") searchindex = int(input('Search result index >> ')) response = search.movie(query=title) try: tmdb_movie = (tmdb.Movies( search.results[searchindex]['id'])) except IndexError: continue # we get the info about the movie response = tmdb_movie.info() # making an imdb object imdb = Imdb() # tmdb_movie.imdb_id is the imdb id of the moovie that we searched # before usng tmdb imdb_movie = imdb.get_title_by_id(tmdb_movie.imdb_id) # using imdb provided movie name and newfilename = (imdb_movie.title + ' (' + str(imdb_movie.year) + ').mp4') newfilename = (newfilename.replace(':', ' -').replace('/', ' ').replace( '?', '')) command = "" if mode == 1: # it is required to rename it as its already an mp4 file that # wasn't proccessed by ffmpeg os.rename(filename, newfilename) if mode == 2 or mode == 4: command = ('ffmpeg -i "' + filename + '" -sub_charenc UTF-8 -i "' + filename[:-4] + '.srt" ' + '-map 0 -map 1 -c copy -c:s mov_text ' '-metadata:s:s:0 handler="English Subtitle" ' '-metadata:s:s:0 language=eng ' '-metadata:s:a:0 handler="" ' '-metadata:s:v:0 handler="" "' + newfilename + '"') subprocess.run(command) if mode == 3: command = ('ffmpeg -i "' + filename + '" -c copy -c:s mov_text ' '-metadata:s:s:0 handler="English" ' '-metadata:s:s:0 language=eng ' '-metadata:s:a:0 handler="" ' '-metadata:s:v:0 handler="" ' '"' + newfilename + '"') subprocess.run(command) # the poster is fetched from tmdb only if there is no file # named " filename + '.jpg' " in the working directory # this way user can provide their own poster image to be used poster_filename = filename[:-4] + '.jpg' if not os.path.isfile(poster_filename): print('\nFetching the movie poster...') path = search.results[searchindex]['poster_path'] poster_path = r'https://image.tmdb.org/t/p/w640' + path uo = urllib.request.urlopen(poster_path) with open(poster_filename, "wb") as poster_file: poster_file.write(uo.read()) poster_file.close() imdb_rating_and_plot = str('IMDb rating [' + str(float(imdb_movie.rating)) + '/10] - ' + imdb_movie.plot_outline) # setting the genres of the movie. I use ';' as a delimeter # to searate the multiple genre values genre = ';'.join(imdb_movie.genres) # Going overboard and adding directors name to artist tag of # the mp4 file directors = imdb_movie.directors_summary director = directors[0].name video = MP4(newfilename) with open(poster_filename, "rb") as f: video["covr"] = [ MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG) ] video['\xa9day'] = str(imdb_movie.year) video['\xa9nam'] = imdb_movie.title video['\xa9cmt'] = imdb_rating_and_plot video['\xa9gen'] = genre video['\xa9ART'] = director print('\nAdding poster and tagging file...') try: video.save() # I have encounterd this error in pevious version # of script, now I handle it by removing the metadata # of the file. That seems to solve the probelem except OverflowError: remove_meta_command = ('ffmpeg -i "' + newfilename + '" -codec copy -map_metadata -1 "' + newfilename[:-4] + 'new.mp4"') subprocess.run(remove_meta_command) video_new = MP4(newfilename[:-4] + 'new.mp4') with open(poster_filename, "rb") as f: video_new["covr"] = [ MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG) ] video_new['\xa9day'] = str(imdb_movie.year) video_new['\xa9nam'] = imdb_movie.title video_new['\xa9cmt'] = imdb_rating_and_plot video_new['\xa9gen'] = genre video_new['\xa9ART'] = director print('\nAdding poster and tagging file...') try: video_new.save() if not os.path.exists('auto fixed files'): os.makedirs('auto fixed files') os.rename(newfilename[:-4] + 'new.mp4', 'auto fixed files\\' + newfilename[:-4] + '.mp4') os.remove(newfilename) except OverflowError: errored_files.append(filename + (' - Could not save even after' 'striping metadata')) continue os.remove(poster_filename) print('\n' + filename + (' was proccesed successfuly!\n\n====================' '======================================')) except Exception as e: print('\nSome error occured while processing ' + filename + '\n\n====================================================') errored_files.append(filename + ' - ' + str(e))
def _add_mp4_image(audio: File, image: Image, image_data): image_format = _mp4_formats.get(image.format, MP4Cover.FORMAT_JPEG) covr = [MP4Cover(image_data, image_format)] audio.tags['covr'] = covr audio.save()
def searchITunes(path): # list of all m4a files in the input dir audioFiles = glob.glob(path + "/*.m4a") tracks=[] for file in audioFiles: # interpret the file as a MP4 file tracks.append(MP4(file)) if len(tracks) == 0: return True # If the 'covr' metadata is already present then do not overwrite the # album art. In this case we do nothing if 'covr' in tracks[0].keys() and len(tracks[0]['covr']) != 0: return True # Get the album and artists meta data fields album = str(tracks[0].tags["\xa9alb"][0]) artist tracks[0].tags["\xa9ART"][0].encode('utf-8').strip() # Some artists metadata contains annoying "featuring" blah # In these cases, try to remove the feat part # Not 100% robust, there may be weird cases I haven't thought of! feat = artist.find(" Feat.") if feat >0 : artist = artist[:feat] searchstr = album + "+" + artist # Another list of annoying special chars to replace for s in [" ", "&", "'", ",", "."]: searchstr=searchstr.replace(s, "+") # If there was a double replace, we end up with ++, which is also bad tmpStr=searchstr.replace("++", "+") while tmpStr != searchstr: searchstr = tmpStr tmpStr = searchstr.replace("++", "+") # This is our final constructed iTunes query search=urllib2.urlopen("https://itunes.apple.com/search?term="+searchstr+"&entity=album&media=music&country=GB") # The iTunes store search returns a string which is itself a valid # python dict. We want to execute that python to load the dict. # First strip the first 3 chars, which are bogus result=search.read()[3:] # Then construct a string that when executed assigns the dict to albumdata result="albumdata="+result # Now we can execute the string. We have to trust Apple here I guess :/ module = imp.new_module("albumdata") exec result in module.__dict__ albumdata=module.albumdata id=0 # If the resultCount key is smpty then there aren't any valid results. Nothing to do if albumdata["resultCount"] == 0: print "no iTunes result found for " + searchstr return False # If there is more than one result, then we want to find the best fit one # Simply choose the one with the best match to the album name elif albumdata["resultCount"] != 1: counter=0 # 'results' is a list of dicts, which themselves should contain the 'collectionName' for result in albumdata['results']: # If the 'collectionName' exactly matches the desired album, choose that one if result['collectionName'] == album: id=counter break counter += 1 # Print the artist and album from teh matching results print "artist, album = " + albumdata['results'][id]['artistName'] + ", " + albumdata['results'][id]['collectionName'] # And this is now a url to the album art in the iTunes store. Request 1080*1080 resolution :) imageURL = albumdata['results'][id]['artworkUrl100'].replace("100x100", "1080x1080") # Get the image data imageFile = urllib2.urlopen(imageURL) imageData = imageFile.read() # And use mutagen to turn it into an embeddable album art cover = MP4Cover(imageData, MP4Cover.FORMAT_JPEG) # Loop over all tracks we found at the start and embed the art for track in tracks: track['covr'] = [cover] track.save() imageFile.close() return True
def Utils_Meta_setMusicInfo(path, info): """ TODO: Write lyrics to file :param path:文件目录 :param info:字典,详情: { "TALB": "Name of Album", "TIT2": "Title", "TPE1": "Author,Separate with '/'", "APIC": "Path to cover photo", "STRICT": Boolean:strict cover mode, "TRANSCODE": Boolean:convert to mp3, "TRANSPATH": "Path to converted file" } :return: int { 0: Nothing need done 1: Need reExt } """ status_code = 0 try: id3 = ID3(path) id3.update_to_v23() id3.delall("TALB") id3.delall("TIT2") id3.delall("TPE1") id3.delall("APIC") id3.add(TALB(encoding=3, text=info["TALB"])) id3.add(TIT2(encoding=3, text=info["TIT2"])) id3.add(TPE1(encoding=3, text=info["TPE1"])) if info["STRICT"]: image = Image.open(info["APIC"]) img_bytes = io.BytesIO() if image.size[0] > image.size[1]: image = image.crop( (int((image.size[0] - image.size[1]) / 2), 0, int((image.size[0] + image.size[1]) / 2), image.size[1])) elif image.size[0] < image.size[1]: image = image.crop((0, int( (image.size[1] - image.size[0]) / 2), 0, int((image.size[0] + image.size[1]) / 2))) image.resize((300, 300)).save(img_bytes, format="JPEG") id3.add( APIC(encoding=0, mime=mimetypes.guess_type(info["APIC"])[0], type=6, data=img_bytes.getvalue())) else: with open(info["APIC"], "rb") as f: id3.add( APIC(encoding=0, mime=mimetypes.guess_type(info["APIC"])[0], type=6, data=f.read())) id3.save() except ID3NoHeaderError: traceback.print_exc() ext = os.path.splitext(path)[1] if ".flac" in ext or ".FLAC" in ext: flac = FLAC(path) flac.tags['TITLE'] = info["TIT2"] flac.tags['ALBUM'] = info["TALB"] flac.tags['ARTIST'] = info["TPE1"] with open(info["APIC"], "rb") as f: image = Image.open(info["APIC"]) p = Picture() p.data = f.read() p.type = 3 p.mime = mimetypes.guess_type(info["APIC"])[0] p.width = image.size[0] p.height = image.size[1] p.depth = 24 # color depth flac.add_picture(p) image.close() flac.save() else: try: mp4 = MP4(path) mp4.tags["\xa9alb"] = info["TALB"] mp4.tags["\xa9nam"] = info["TIT2"] mp4.tags["\xa9ART"] = info["TPE1"] with open(info["APIC"], "rb") as f: mp4["covr"] = [ MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_PNG) ] mp4.save() status_code = 1 except Exception: traceback.print_exc() if info["TRANSCODE"]: if not os.path.exists(os.path.split(info["TRANSPATH"])[0]): os.makedirs(os.path.split(info["TRANSPATH"])[0]) Utils_FormatTools.Utils_Format_autoTranscode( path, info["TRANSPATH"]) info["TRANSCODE"] = False Utils_Meta_setMusicInfo(info["TRANSPATH"], info) except MutagenError: traceback.print_exc() return status_code
def save_song_attrs_to_mp4tags(tags, song): # - covr: Artwork if song.artwork: tags["covr"] = [ MP4Cover(song.artwork.read(), imageformat=MP4Cover.FORMAT_JPEG) ] # - \xa9nam: Song Title if song.title: tags["\xa9nam"] = song.title.decode('unicode-escape') # - \xa9ART: Artist if song.artist: tags["\xa9ART"] = song.artist.decode('unicode-escape') # - \xa9day: Year if song.year: tags["\xa9day"] = str(song.year).decode('unicode-escape') # - \xa9alb: Album if song.album: tags["\xa9alb"] = song.album.decode('unicode-escape') # - Release date? # release_date = # - aART: Album artist if song.album_artist: tags["aART"] = song.album_artist.decode('unicode-escape') # - trkn: Track number if song.track_number: tags["trkn"] = str(song.track_number).decode('unicode-escape') # - tmpo: BPM if song.bpm: tags["tmpo"] = str(song.bpm).decode('unicode-escape') # - Original artist # if song.original_artist: # ??? # - Key # if song.key: # ??? # - \xa9wrt: Composer if song.composer: tags["\xa9wrt"] = song.composer.decode('unicode-escape') # - \xa9lyr: Lyricist if song.lyrics: tags["\xa9lyr"] = song.lyrics.decode('unicode-escape') # - \xa9cmt: Comments if song.comments: tags["\xa9cmt"] = song.comments.decode('unicode-escape') # - Remixer # if song.remixer: # ??? # - Label/Publisher # if song.label: # ??? # - \xa9gen: Genre/content type if song.genre: tags["\xa9gen"] = song.genre.decode('unicode-escape')
def run(self, info): filename = info['filepath'] temp_filename = prepend_extension(filename, 'temp') if not info.get('thumbnails'): self.to_screen('There aren\'t any thumbnails to embed') return [], info idx = next((-i for i, t in enumerate(info['thumbnails'][::-1], 1) if t.get('filepath')), None) if idx is None: self.to_screen('There are no thumbnails on disk') return [], info thumbnail_filename = info['thumbnails'][idx]['filepath'] if not os.path.exists(encodeFilename(thumbnail_filename)): self.report_warning( 'Skipping embedding the thumbnail because the file is missing.' ) return [], info # Correct extension for WebP file with wrong extension (see #25687, #25717) convertor = FFmpegThumbnailsConvertorPP(self._downloader) convertor.fixup_webp(info, idx) original_thumbnail = thumbnail_filename = info['thumbnails'][idx][ 'filepath'] # Convert unsupported thumbnail formats to PNG (see #25687, #25717) # Original behavior was to convert to JPG, but since JPG is a lossy # format, there will be some additional data loss. # PNG, on the other hand, is lossless. thumbnail_ext = os.path.splitext(thumbnail_filename)[1][1:] if thumbnail_ext not in ('jpg', 'jpeg', 'png'): thumbnail_filename = convertor.convert_thumbnail( thumbnail_filename, 'png') thumbnail_ext = 'png' mtime = os.stat(encodeFilename(filename)).st_mtime success = True if info['ext'] == 'mp3': options = [ '-c', 'copy', '-map', '0:0', '-map', '1:0', '-id3v2_version', '3', '-metadata:s:v', 'title="Album cover"', '-metadata:s:v', 'comment="Cover (front)"' ] self._report_run('ffmpeg', filename) self.run_ffmpeg_multiple_files([filename, thumbnail_filename], temp_filename, options) elif info['ext'] in ['mkv', 'mka']: options = ['-c', 'copy', '-map', '0', '-dn'] mimetype = 'image/%s' % ('png' if thumbnail_ext == 'png' else 'jpeg') old_stream, new_stream = self.get_stream_number( filename, ('tags', 'mimetype'), mimetype) if old_stream is not None: options.extend(['-map', '-0:%d' % old_stream]) new_stream -= 1 options.extend([ '-attach', thumbnail_filename, '-metadata:s:%d' % new_stream, 'mimetype=%s' % mimetype, '-metadata:s:%d' % new_stream, 'filename=cover.%s' % thumbnail_ext ]) self._report_run('ffmpeg', filename) self.run_ffmpeg(filename, temp_filename, options) elif info['ext'] in ['m4a', 'mp4', 'mov']: prefer_atomicparsley = 'embed-thumbnail-atomicparsley' in self.get_param( 'compat_opts', []) # Method 1: Use mutagen if not has_mutagen or prefer_atomicparsley: success = False else: try: self._report_run('mutagen', filename) meta = MP4(filename) # NOTE: the 'covr' atom is a non-standard MPEG-4 atom, # Apple iTunes 'M4A' files include the 'moov.udta.meta.ilst' atom. f = { 'jpeg': MP4Cover.FORMAT_JPEG, 'png': MP4Cover.FORMAT_PNG }[imghdr.what(thumbnail_filename)] with open(thumbnail_filename, 'rb') as thumbfile: thumb_data = thumbfile.read() meta.tags['covr'] = [ MP4Cover(data=thumb_data, imageformat=f) ] meta.save() temp_filename = filename except Exception as err: self.report_warning('unable to embed using mutagen; %s' % error_to_compat_str(err)) success = False # Method 2: Use ffmpeg+ffprobe if not success and not prefer_atomicparsley: success = True try: options = ['-c', 'copy', '-map', '0', '-dn', '-map', '1'] old_stream, new_stream = self.get_stream_number( filename, ('disposition', 'attached_pic'), 1) if old_stream is not None: options.extend(['-map', '-0:%d' % old_stream]) new_stream -= 1 options.extend( ['-disposition:%s' % new_stream, 'attached_pic']) self._report_run('ffmpeg', filename) self.run_ffmpeg_multiple_files( [filename, thumbnail_filename], temp_filename, options) except PostProcessingError as err: self.report_warning( 'unable to embed using ffprobe & ffmpeg; %s' % error_to_compat_str(err)) success = False # Method 3: Use AtomicParsley if not success: success = True atomicparsley = next( (x for x in ['AtomicParsley', 'atomicparsley'] if check_executable(x, ['-v'])), None) if atomicparsley is None: raise EmbedThumbnailPPError( 'AtomicParsley was not found. Please install') cmd = [ encodeFilename(atomicparsley, True), encodeFilename(filename, True), encodeArgument('--artwork'), encodeFilename(thumbnail_filename, True), encodeArgument('-o'), encodeFilename(temp_filename, True) ] cmd += [ encodeArgument(o) for o in self._configuration_args('AtomicParsley') ] self._report_run('atomicparsley', filename) self.write_debug('AtomicParsley command line: %s' % shell_quote(cmd)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process_communicate_or_kill(p) if p.returncode != 0: msg = stderr.decode('utf-8', 'replace').strip() raise EmbedThumbnailPPError(msg) # for formats that don't support thumbnails (like 3gp) AtomicParsley # won't create to the temporary file if b'No changes' in stdout: self.report_warning( 'The file format doesn\'t support embedding a thumbnail' ) success = False elif info['ext'] in ['ogg', 'opus', 'flac']: if not has_mutagen: raise EmbedThumbnailPPError( 'module mutagen was not found. Please install using `python -m pip install mutagen`' ) self._report_run('mutagen', filename) f = { 'opus': OggOpus, 'flac': FLAC, 'ogg': OggVorbis }[info['ext']](filename) pic = Picture() pic.mime = 'image/%s' % imghdr.what(thumbnail_filename) with open(thumbnail_filename, 'rb') as thumbfile: pic.data = thumbfile.read() pic.type = 3 # front cover res = self._get_thumbnail_resolution(thumbnail_filename, info['thumbnails'][idx]) if res is not None: pic.width, pic.height = res if info['ext'] == 'flac': f.add_picture(pic) else: # https://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE f['METADATA_BLOCK_PICTURE'] = base64.b64encode( pic.write()).decode('ascii') f.save() temp_filename = filename else: raise EmbedThumbnailPPError( 'Supported filetypes for thumbnail embedding are: mp3, mkv/mka, ogg/opus/flac, m4a/mp4/mov' ) if success and temp_filename != filename: os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) self.try_utime(filename, mtime, mtime) files_to_delete = [thumbnail_filename] if self._already_have_thumbnail: if original_thumbnail == thumbnail_filename: files_to_delete = [] elif original_thumbnail != thumbnail_filename: files_to_delete.append(original_thumbnail) return files_to_delete, info
def writeTags(self, mp4Path, artwork=True, thumbnail=False): self.log.info("Tagging file: %s." % mp4Path) ext = os.path.splitext(mp4Path)[1][1:] if ext not in valid_tagging_extensions: self.log.error("File is not the correct format.") return False video = MP4(mp4Path) checktags = {} checktags["\xa9nam"] = self.title # Movie title checktags["desc"] = self.shortdescription # Short description checktags["ldes"] = self.description # Long description checktags["\xa9day"] = self.date # Year #checktags["stik"] = [9] # Movie iTunes category #if self.HD is not None: # checktags["hdvd"] = self.HD if self.genre is not None: genre = None for g in self.genre: if genre is None: genre = g['name'] break # else: # genre += ", " + g['name'] checktags["\xa9gen"] = genre # Genre(s) #checktags["----:com.apple.iTunes:iTunMOVI"] = self.xml.encode("UTF-8", errors="ignore") # XML - see xmlTags method ''' rating = self.rating() if rating is not None: checktags["----:com.apple.iTunes:iTunEXTC"] = rating ''' if artwork: path = self.getArtwork(mp4Path) if path is not None: cover = open(path, 'rb').read() if path.endswith('png'): checktags["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG)] # png poster else: checktags["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_JPEG)] # jpeg poster ProcessTags = False for keys, values in checktags.items(): if video.tags == None: ProcessTags = True break elif keys in video.tags: if video.tags[keys] != [values]: self.log.info(keys + " tag does not match and will be updated") ProcessTags = True break else: self.log.debug(keys + " will be added") ProcessTags = True if not ProcessTags: self.log.info("All MP4 tags match the original, skipping tagging.") else: try: video.delete() except IOError: self.log.debug("Unable to clear original tags, attempting to proceed.") video["\xa9nam"] = self.title # Movie title video["desc"] = self.shortdescription # Short description video["ldes"] = self.description # Long description video["\xa9day"] = self.date # Year #video["stik"] = [9] # Movie iTunes category #if self.HD is not None: # video["hdvd"] = self.HD if self.genre is not None: genre = None for g in self.genre: if genre is None: genre = g['name'] break # else: # genre += ", " + g['name'] video["\xa9gen"] = genre # Genre(s) #video["----:com.apple.iTunes:iTunMOVI"] = self.xml.encode("UTF-8", errors="ignore") # XML - see xmlTags method ''' rating = self.rating() if rating is not None: video["----:com.apple.iTunes:iTunEXTC"] = rating ''' if artwork: path = self.getArtwork(mp4Path) if path is not None: cover = open(path, 'rb').read() if path.endswith('png'): video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG)] # png poster else: video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_JPEG)] # jpeg poster if self.original: video["\xa9too"] = "MDH:" + os.path.basename(self.original) else: video["\xa9too"] = "MDH:" + os.path.basename(mp4Path) for i in range(3): try: self.log.info("Trying to write tags.") video.save() self.log.info("Tags written successfully.") return True except IOError as e: self.log.info("Exception: %s" % e) self.log.exception("There was a problem writing the tags. Retrying.") time.sleep(5) return False
def writeTags(self, path, converter, artwork=True, thumbnail=False, width=None, height=None, streaming=0): self.log.info("Tagging file: %s." % path) if width and height: try: self.setHD(width, height) except: self.log.exception("Unable to set HD tag.") try: video = MP4(path) except (MP4StreamInfoError, KeyError): self.log.debug('File is not a valid MP4 file and cannot be tagged using mutagen, falling back to FFMPEG limited tagging.') try: metadata = {} if self.mediatype == MediaType.Movie: metadata['TITLE'] = self.title # Movie title metadata["COMMENT"] = self.description # Long description metadata["DATE_RELEASE"] = self.date # Year metadata["DATE"] = self.date # Year elif self.mediatype == MediaType.TV: metadata['TITLE'] = self.title # Video title metadata["COMMENT"] = self.description # Long description metadata["DATE_RELEASE"] = self.date # Air Date metadata["DATE"] = self.date # Air Date metadata["ALBUM"] = self.showname + ", Season " + str(self.season) # Album as Season if self.genre and len(self.genre) > 0: metadata["GENRE"] = self.genre[0].get('name') metadata["ENCODER"] = "SMA" coverpath = None if artwork: coverpath = self.getArtwork(path, thumbnail=thumbnail) try: conv = converter.tag(path, metadata, coverpath, streaming) except KeyboardInterrupt: raise except: self.log.exception("FFMPEG Tag Error.") return False _, cmds = next(conv) self.log.debug("Metadata tagging FFmpeg command:") self.log.debug(" ".join(str(item) for item in cmds)) for timecode, debug in conv: self.log.debug(debug) self.log.info("Tags written successfully using FFMPEG fallback method.") return True except KeyboardInterrupt: raise except: self.log.exception("Unexpected tagging error using FFMPEG fallback method.") return False try: video.delete() except KeyboardInterrupt: raise except: self.log.debug("Unable to clear original tags, will proceed.") if self.mediatype == MediaType.Movie: video["\xa9nam"] = self.title # Movie title video["desc"] = self.tagline # Short description video["ldes"] = self.description # Long description video["\xa9day"] = self.date # Year video["stik"] = [9] # Movie iTunes category elif self.mediatype == MediaType.TV: video["tvsh"] = self.showname # TV show title video["\xa9nam"] = self.title # Video title video["tven"] = self.title # Episode title video["desc"] = self.shortDescription # Short description video["ldes"] = self.description # Long description network = [x['name'] for x in self.network] video["tvnn"] = network # Network video["\xa9day"] = self.date # Air Date video["tvsn"] = [self.season] # Season number video["disk"] = [(self.season, 0)] # Season number as disk video["\xa9alb"] = self.showname + ", Season " + str(self.season) # iTunes Album as Season video["tves"] = [self.episode] # Episode number video["trkn"] = [(self.episode, len(self.seasondata.get('episodes', [])))] # Episode number iTunes video["stik"] = [10] # TV show iTunes category if self.HD: video["hdvd"] = self.HD if self.genre and len(self.genre) > 0: video["\xa9gen"] = self.genre[0].get('name') video["----:com.apple.iTunes:iTunMOVI"] = self.xml.encode("UTF-8", errors="ignore") # XML - see xmlTags method if self.rating: video["----:com.apple.iTunes:iTunEXTC"] = self.rating.encode("UTF-8", errors="ignore") # iTunes content rating if artwork: coverpath = self.getArtwork(path, thumbnail=thumbnail) if coverpath is not None: cover = open(coverpath, 'rb').read() if coverpath.endswith('png'): video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG)] # png poster else: video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_JPEG)] # jpeg poster if self.original: video["\xa9too"] = "SMA:" + os.path.basename(self.original) else: video["\xa9too"] = "SMA:" + os.path.basename(path) try: self.log.info("Trying to write tags.") video.save() self.log.info("Tags written successfully using mutagen.") return True except KeyboardInterrupt: raise except: self.log.exception("There was an error writing the tags.") return False
def writeTags(self, mp4Path, artwork=True): self.log.info("Tagging file: %s." % mp4Path) ext = os.path.splitext(mp4Path)[1][1:] if ext not in valid_output_extensions: self.log.error("File is not the correct format.") sys.exit() try: MP4(mp4Path).delete() except IOError: self.log.debug( "Unable to clear original tags, attempting to proceed.") video = MP4(mp4Path) video["tvsh"] = self.show # TV show title video["\xa9nam"] = self.title # Video title video["tven"] = self.title # Episode title video["desc"] = self.shortDescription() # Short description video[ "ldes"] = self.description # Long description (same a short for tv) video["tvnn"] = self.network # Network if self.airdate != "0000-00-00": video["\xa9day"] = self.airdate # Airdate video["tvsn"] = [self.season] # Season number video["disk"] = [(int(self.season), 0)] # Season number as disk video["\xa9alb"] = self.show + ", Season " + str( self.season) # iTunes Album as Season video["tves"] = [self.episode] # Episode number video["trkn"] = [(int(self.episode), len(self.seasondata)) ] # Episode number iTunes video["stik"] = [10] # TV show iTunes category if self.HD is not None: video["hdvd"] = self.HD if self.genre is not None: video["\xa9gen"] = self.genre[1:-1].split('|')[0] #video["\xa9gen"] = self.genre.replace('|', ',')[1:-1] # Genre(s) video[ "----:com.apple.iTunes:iTunMOVI"] = self.xml # XML - see xmlTags method video["----:com.apple.iTunes:iTunEXTC"] = self.setRating( ) # iTunes content rating if artwork: path = self.getArtwork(mp4Path) if path is not None: cover = open(path, 'rb').read() if path.endswith('png'): video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_PNG) ] # png poster else: video["covr"] = [MP4Cover(cover, MP4Cover.FORMAT_JPEG) ] # jpeg poster if self.original: video["\xa9too"] = "MDH:" + os.path.basename(self.original) else: video["\xa9too"] = "MDH:" + os.path.basename(mp4Path) MP4(mp4Path).delete(mp4Path) for i in range(3): try: self.log.info("Trying to write tags.") video.save() self.log.info("Tags written successfully.") break except IOError as e: self.log.exception( "There was a problem writing the tags. Retrying.") time.sleep(5)
def processFilm(film_id, filepath): global subtitlesActioned subtitlesFound = subtitlesExistForItem(filepath) fileExtPattern = re.compile('\.[a-zA-Z0-9]+$') oldfilePath = filepath ext = fileExtPattern.findall(filepath)[0] newfilepath = filepath.replace(ext, '-with-subs' + ext) print("\n\n========\n\n") print(filepath) print(newfilepath) print(oldfilePath) if subtitlesFound and subtitlesActioned == False: subtitlesActioned = True print("\n\n\nCALLING " + " ".join([ 'ffmpeg', '-i', oldfilePath, '-i', subtitlesFound, '-c:s mov_text', newfilepath ])) print("\n\n\n\n") subprocess.call([ 'ffmpeg', '-i', oldfilePath, '-i', subtitlesFound, '-c:s mov_text', newfilepath ]) filetags = checkTags(filepath) if subtitlesActioned: # tagged_file = MP4(newfilepath) print("true") else: tagged_file = MP4(oldfilePath) print(tagged_file.tags) data = getFilmData(film_id) tagged_file['stik'] = [9] genres = [] if 'genres' in data: for genre in data['genres']: genres.append(genre['name']) tagged_file['\xa9gen'] = genres if 'overview' in data: tagged_file['ldes'] = data['overview'] tagged_file['desc'] = data['overview'] if 'title' in data: tagged_file['\xa9alb'] = data['title'] tagged_file['aART'] = data['title'] tagged_file['\xa9nam'] = data['title'] elif 'original_title' in data: tagged_file['\xa9alb'] = data['original_title'] tagged_file['aART'] = data['original_title'] tagged_file['\xa9nam'] = data['original_title'] if 'poster_path' in data: with open(data['poster_path'][1:], "rb") as f: tagged_file["covr"] = [ MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG) ] if 'release_date' in data: tagged_file['\xa9day'] = data['release_date'][:4] # GET CAST AND CREW cast_crew_data = getCastandCrew(film_id, "movie") cast = [] directors = [] screenwriters = [] producers = [] producer_re = re.compile("Producer$") for cast_member in cast_crew_data['cast']: cast.append(cast_member['name']) for crew_members in cast_crew_data['crew']: if crew_members['job'] == "Director": directors.append(crew_members['name']) if crew_members['department'] == "Writing": screenwriters.append(crew_members['name']) if producer_re.search(crew_members['job']): producers.append(crew_members['name']) xml_str = "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" xml_str += "<plist version=\"1.0\">\n" xml_str += "<dict>" xml_str += generateXML(cast, "cast") xml_str += generateXML(directors, "directors") xml_str += generateXML(screenwriters, "screenwriters") xml_str += generateXML(producers, "producers") xml_str += "</dict>" xml_str += "</plist>" tagged_file['----:com.apple.iTunes:iTunMOVI'] = str.encode(xml_str) # hdvd # 0 - nothing # 1 - 720p # 2 - 1080p # 3 - 4K vid = cv2.VideoCapture(filepath) height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT) width = vid.get(cv2.CAP_PROP_FRAME_WIDTH) print(width) if width > 1919 and width < 3839: tagged_file['hdvd'] = [2] elif width < 1919 and width > 1279: tagged_file['hdvd'] = [1] elif width > 3839: tagged_file['hdvd'] = [3] else: tagged_file['hdvd'] = [0] # GENERATE XML AND AS AS BITES TO ----:com.apple.iTunes:iTunMOVI TAG rating = getClassification(film_id) tagged_file['----:com.apple.iTunes:iTunEXTC'] = str.encode("b'mpaa|" + rating + "|300|") tagged_file.save()
def test_cover_png(self): self.set_key('covr', [ MP4Cover(b'woooo', MP4Cover.FORMAT_PNG), MP4Cover(b'hoooo', MP4Cover.FORMAT_JPEG), ])
def applyData(data, filepath, show_id, filename): global season_artwork, season_data, show_data print(data) tagged_file = MP4(filepath) tagged_file['stik'] = [10] if 'season_number' in data: tagged_file['tvsn'] = [data['season_number']] if 'episode_number' in data: tagged_file['tves'] = [data['episode_number']] if season_artwork != "": with open(season_artwork, "rb") as f: tagged_file["covr"] = [ MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG) ] if 'air_date' in data: tagged_file['\xa9day'] = data['air_date'][:4] if 'name' in data: tagged_file['\xa9nam'] = data['name'] if 'overview' in data: tagged_file['desc'] = data['overview'] if 'original_name' in show_data: tagged_file['tvsh'] = show_data['original_name'] tagged_file['\xa9alb'] = show_data['original_name'] tagged_file['\xa9ART'] = show_data['original_name'] tagged_file['aART'] = show_data['original_name'] vid = cv2.VideoCapture(filepath) height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT) width = vid.get(cv2.CAP_PROP_FRAME_WIDTH) print(width) if width > 1919 and width < 3839: tagged_file['hdvd'] = [2] elif width < 1919 and width > 1279: tagged_file['hdvd'] = [1] elif width > 3839: tagged_file['hdvd'] = [3] else: tagged_file['hdvd'] = [0] cast_crew_data = getCastandCrew(show_id, "tv") cast = [] directors = [] screenwriters = [] producers = [] producer_re = re.compile("Producer$") for cast_member in cast_crew_data['cast']: cast.append(cast_member['name']) for crew_members in cast_crew_data['crew']: if crew_members['job'] == "Director": directors.append(crew_members['name']) if crew_members['department'] == "Writing": screenwriters.append(crew_members['name']) if producer_re.search(crew_members['job']): producers.append(crew_members['name']) xml_str = "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" xml_str += "<plist version=\"1.0\">\n" xml_str += "<dict>" xml_str += generateXML(cast, "cast") xml_str += generateXML(directors, "directors") xml_str += generateXML(screenwriters, "screenwriters") xml_str += generateXML(producers, "producers") xml_str += "</dict>" xml_str += "</plist>" tagged_file['----:com.apple.iTunes:iTunMOVI'] = str.encode(xml_str) rating = getTVContentRating(show_id) tagged_file['----:com.apple.iTunes:iTunEXTC'] = str.encode("b'mpaa|" + rating + "|300|") genres = [] if 'genres' in show_data: for genre in show_data['genres']: genres.append(genre['name']) tagged_file['\xa9gen'] = genres tagged_file.save()
def test_cmp_bytes(self): self.assertReallyEqual(MP4Cover(b'woooo'), b"woooo") self.assertReallyNotEqual(MP4Cover(b'woooo'), b"foo")
def write_metadata(self, filename, force=False): # MP4 needs these attributes as TXXX. for value in ('original album', 'original_artist', 'original_year'): self.txxx[value.upper().replace('_', ' ')] = value track = self.track log.debug("Writing metadata for: %s", filename) if os.path.exists(filename) is False: log.warn("Couldn't find MP4 file!: %s", filename) return None self.tags = mutagen.mp4.MP4(filename) if not force and self.is_up_to_date(): log.debug('Up to date: "%s"' % filename) return None # Clear out any old data. self.tags.clear() # Basics first. for atom, key in self.attributes.items(): if hasattr(track, key): value = getattr(track, key, None) log.debug("Trying: key: %s (%s)", key, value) if isinstance(value, str): value = value.encode('utf-8') # 'tmpo' needs to be a list of integers. if key == 'bpm' and value is not None: value = [int(value)] if value is not None: self.tags[atom] = value # Hack alert.. not sure how better to "detect" this. if track.genre: for genre in list(track.genre): if genre in self.gapless: self.tags["pgap"] = True if track.compilation: self.tags["cpil"] = True if track.tracknumber and track.tracktotal: self.tags["trkn"] = [(track.tracknumber, track.tracktotal)] elif track.tracknumber: self.tags["trkn"] = [(track.tracknumber, 0)] # Convert RG tags into iTunes SoundCheck # TODO: Find what tags aacgain uses as well. if track.replaygain_album_gain: self.tags['----:com.apple.iTunes:iTunNORM'] = replay_gain_to_soundcheck(track.replaygain_album_gain, track.replaygain_album_peak) elif track.replaygain_track_gain: self.tags['----:com.apple.iTunes:iTunNORM'] = replay_gain_to_soundcheck(track.replaygain_track_gain, track.replaygain_track_peak) # if track.discnumber and track.disctotal: try: self.tags["disk"] = [(track.discnumber, track.disctotal)] except ValueError: pass elif track.disctotal: self.tags["disk"] = [(track.disctotal, track.disctotal)] # Artwork time.. if track.cover_file and os.path.exists(track.cover_file): with open(track.cover_file, 'rb') as fh: if track.cover_file.endswith(".png"): self.tags["covr"] = [ MP4Cover(fh.read(), MP4Cover.FORMAT_PNG) ] elif track.cover_file.endswith(".jpg"): self.tags["covr"] = [ MP4Cover(fh.read(), MP4Cover.FORMAT_JPEG) ] # Always add the check & time stamp for next time. if track.checksum: self.tags['----:com.sully.flac2mp4:checksum'] = str(track.checksum) self.tags['----:com.sully.flac2mp4:flacmtime'] = str(track.mtime) # Convert all user defined tags. for tag, attribute in self.txxx.items(): if getattr(track, attribute, None): self.tags['----:com.apple.iTunes:' + tag] = getattr(track, attribute).encode('utf-8') try: self.tags.save(filename) except Exception as e: log.warn("Couldn't save file %s: %s", filename, e)