def _audio_time(atuple): audio, atag, advanced, _, _ = atuple if advanced: param = ast.literal_eval(atag) audio.add(TIME(3, param[1])) else: audio.add(TIME(3, atag))
def test_tyer_tdat_time(self): id3 = ID3() id3.version = (2, 3) id3.add(TYER(encoding=0, text="2006")) id3.add(TDAT(encoding=0, text="0603")) id3.add(TIME(encoding=0, text="1127")) id3.update_to_v24() self.failUnlessEqual(id3["TDRC"], "2006-03-06 11:27:00")
def test_multiple_tyer_tdat_time(self): id3 = ID3() id3.version = (2, 3) id3.add(TYER(text=['2000', '2001', '2002', '19xx', 'foo'])) id3.add(TDAT(text=['0102', '0304', '1111bar'])) id3.add(TIME(text=['1220', '1111quux', '1111'])) id3.update_to_v24() assert [str(t) for t in id3['TDRC']] == \ ['2000-02-01 12:20:00', '2001-04-03', '2002']
def update_to_v23(self, join_with="/"): """Convert older (and newer) tags into an ID3v2.3 tag. This updates incompatible ID3v2 frames to ID3v2.3 ones. If you intend to save tags as ID3v2.3, you must call this function at some point. """ if self.version < (2, 3, 0): del self.unknown_frames[:] # TMCL, TIPL -> TIPL if "TIPL" in self or "TMCL" in self: people = [] if "TIPL" in self: f = self.pop("TIPL") people.extend(f.people) if "TMCL" in self: f = self.pop("TMCL") people.extend(f.people) if "IPLS" not in self: self.add(IPLS(encoding=f.encoding, people=people)) # TODO: # * EQU2 -> EQUA # * RVA2 -> RVAD # TDOR -> TORY if "TDOR" in self: f = self.pop("TDOR") if f.text: d = f.text[0] if d.year and "TORY" not in self: self.add(TORY(encoding=f.encoding, text="%04d" % d.year)) # TDRC -> TYER, TDAT, TIME if "TDRC" in self: f = self.pop("TDRC") if f.text: d = f.text[0] if d.year and "TYER" not in self: self.add(TYER(encoding=f.encoding, text="%04d" % d.year)) if d.month and d.day and "TDAT" not in self: self.add( TDAT(encoding=f.encoding, text="%02d%02d" % (d.day, d.month))) if d.hour and d.minute and "TIME" not in self: self.add( TIME(encoding=f.encoding, text="%02d%02d" % (d.hour, d.minute))) if "TCON" in self: self["TCON"].genres = self["TCON"].genres if self.version < (2, 3): # ID3v2.2 PIC frames are slightly different. pics = self.getall("APIC") mimes = {"PNG": "image/png", "JPG": "image/jpeg"} self.delall("APIC") for pic in pics: newpic = APIC(encoding=pic.encoding, mime=mimes.get(pic.mime, pic.mime), type=pic.type, desc=pic.desc, data=pic.data) self.add(newpic) # ID3v2.2 LNK frames are just way too different to upgrade. self.delall("LINK") # leave TSOP, TSOA and TSOT even though they are officially defined # only in ID3v2.4, because most applications use them also in ID3v2.3 # New frames added in v2.4. for key in [ "ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG", "TMOO", "TPRO" ]: if key in self: del (self[key]) for frame in self.values(): # ID3v2.3 doesn't support UTF-8 (and WMP can't read UTF-16 BE) if hasattr(frame, "encoding"): if frame.encoding > 1: frame.encoding = 1 # ID3v2.3 doesn't support multiple values if isinstance(frame, mutagen.id3.TextFrame): try: frame.text = [join_with.join(frame.text)] except TypeError: frame.text = frame.text[:1]
def test_time_dropped(self): id3 = ID3() id3.version = (2, 3) id3.add(TIME(encoding=0, text=["1155"])) id3.update_to_v24() self.assertFalse(id3.getall("TIME"))
def download(broadcast, targetDir, reliveUrlTemplate): broadcastStartDT = parse(broadcast['start']) broadcastEndDT = parse(broadcast['end']) # build filename from channel, show title and broadcast datetime, while escaping "bad" characters filename = os.path.join( targetDir, re.sub( '[^\w\s\-\.\[\]]', '_', broadcast['trackingInfos']['pageVars']['broadcast_service'] + ' ' + broadcastStartDT.astimezone( pytz.timezone('Europe/Berlin')).strftime("%Y-%m-%d %H:%M") + ' ' + broadcast['trackingInfos']['pageVars']['topline']) + ".mp3") # skip broadcast if file is already exists if os.path.isfile(filename) and os.path.getsize(filename) > 0: print("%s already exists, skipping." % filename, flush=True) return # get links to all audio segments of this broadcast segmentUrls = getSegmentUrls(broadcastStartDT, broadcastEndDT, reliveUrlTemplate) if segmentUrls is None: # skip broadcast if no segments available print("Skipping %s, not yet in relive" % filename) return # dowload all ts segments, and convert them to mp3 print("Downloading %s ..." % filename, end=" ", flush=True) try: sound = AudioSegment.empty() for i in segmentUrls: sound += AudioSegment.from_file(BytesIO(urlopen(i).read())) sound.export(filename, format="mp3") except: print("failed.", flush=True) return else: print("done.", flush=True) # ID3: remove all tags try: tags = ID3(filename) tags.delete() except ID3NoHeaderError: tags = ID3() # ID3: save as much information as possible in the ID3 tags tags.add( TRSN( text=[broadcast['trackingInfos']['pageVars']['broadcast_service'] ])) tags.add( TPE1( text=[broadcast['trackingInfos']['pageVars']['broadcast_service'] ])) tags.add( TALB(text=[ " - ".join( list( dict.fromkeys([ broadcast['trackingInfos']['pageVars']['topline'], broadcast['trackingInfos']['pageVars']['title'] ]))) ])) tags.add(TRCK(text=['1/1'])) #tags.add(TIT2(text=[broadcastStartDT.astimezone(pytz.timezone('Europe/Berlin')).strftime("%Y-%m-%d %H:%M")])) tags.add(TIT2(text=[broadcast['publicationOf']['title']])) tags.add( COMM(lang="deu", desc="desc", text=[broadcast['publicationOf']['description']])) tags.add( TYER(text=[ broadcastStartDT.astimezone(pytz.timezone( 'Europe/Berlin')).strftime("%Y") ])) tags.add( TDAT(text=[ broadcastStartDT.astimezone(pytz.timezone( 'Europe/Berlin')).strftime("%d%m") ])) tags.add( TIME(text=[ broadcastStartDT.astimezone(pytz.timezone( 'Europe/Berlin')).strftime("%H%M") ])) tags.add( TLEN(text=[ int((broadcastEndDT - broadcastStartDT).total_seconds() * 1000) ])) tags.add(WOAS(url=broadcast['publicationOf']['canonicalUrl'])) tags.add(WORS(url="https://www.br.de/radio/")) # ID3: chapters chapterNr = 0 for chapter in broadcast['items']: chapterStartDT = parse(chapter['start']) if 'duration' in chapter and chapter['duration'] is not None: chapterEndDT = chapterStartDT + timedelta( seconds=chapter['duration']) else: chapterEndDT = broadcastEndDT artists = [] for i in ['performer', 'author']: if i in chapter and chapter[i] is not None and len(chapter[i]) > 0: artists.append(chapter[i]) titles = [] for i in ['title']: if i in chapter and chapter[i] is not None and len(chapter[i]) > 0: titles.append(chapter[i]) tags.add( CHAP(element_id=chapterNr, start_time=floor( (chapterStartDT - broadcastStartDT).total_seconds() * 1000), end_time=ceil( (chapterEndDT - broadcastStartDT).total_seconds() * 1000), sub_frames=[ TIT2(text=[ " - ".join([" ".join(artists), " ".join(titles)]) ]) ])) chapterNr += 1 tocList = ",".join([str(i) for i in range(0, chapterNr)]) tags.add( CTOC(element_id="toc", flags=CTOCFlags.TOP_LEVEL | CTOCFlags.ORDERED, child_element_ids=[tocList], sub_frames=[TIT2(text=["Table Of Contents"])])) # ID3: cover image response = requests.get( broadcast['publicationOf']['defaultTeaserImage']['url'], timeout=5) if response.status_code == 200: tags.add( APIC(mime=response.headers['content-type'], desc="Front Cover", data=response.content)) # save ID3 tags tags.save(filename, v2_version=3)
try: tags = ID3(showInfo['parts'][partNo]['filepath'] + ".part") tags.delete() except ID3NoHeaderError: tags = ID3() tags.add(TRSN(text=[stationInfo['name']])) tags.add(TPE1(text=[stationInfo['name']])) tags.add(TALB(text=[showInfo['name']])) tags.add( TRCK(text=[str(partNo + 1) + "/" + str(len(showInfo['parts']))])) tags.add(TIT2(text=[showInfo['parts'][partNo]['title']])) tags.add(COMM(lang="deu", desc="desc", text=[showInfo['description']])) tags.add(TYER(text=[showInfo['start_dt'].strftime("%Y")])) tags.add(TDAT(text=[showInfo['start_dt'].strftime("%d%m")])) tags.add(TIME(text=[showInfo['start_dt'].strftime("%H%M")])) tags.add(TLEN(text=[showInfo['parts'][partNo]['duration_ms']])) tags.add(WOAS(url=showInfo['website'])) tags.add(WORS(url=stationInfo['website'])) for chapter in showInfo['parts'][partNo]['chapters']: tags.add( CHAP(element_id=chapter["id"], start_time=chapter["start_ms"], end_time=chapter["end_ms"], sub_frames=[TIT2(text=[chapter["title"]])])) tocList = ",".join([ chapter["id"] for chapter in showInfo['parts'][partNo]['chapters'] ]) tags.add(