wmcbrine / pytivo

pyTivo is both an HMO and GoBack server. Similar to TiVo Desktop pyTivo loads many standard video compression codecs and outputs mpeg2 video to the TiVo. However, pyTivo is able to load MANY more file types than TiVo Desktop.
http://pytivo.org/
127 stars 42 forks source link

MP4 metadata improvements #18

Closed mackworth closed 8 years ago

mackworth commented 8 years ago

I've spent quite a while on improving the metadata communication from cTiVo to pyTivo through the MP4 embedded metadata in order to make the roundtrip to TiVo better. I've fixed several items on cTiVo's end, but I think the attached changes would improve pyTiVo's parsing of the metadata. I hope you agree.

Let me start by acknowledging that I'm neither an expert on pyTiVo nor in Python, so happy to make any changes to this pull request that you think are appropriate. And if I've broken something, my apologies.

The changes are:

  1. The MP4 tven (TV episode ID) key is not the same as TiVo's episodeNumber. tven is set by iTunes to SnEn, but that's better read from tvsn and tves; so in TiVo's case, tven should be TiVo's programID. Then episodeNumber is constructed fromtvsn/tves (or programID tven's SnEn format) as I think TiVo expects it to be numeric.
  2. AFAIK, Tivo's isEpisode is true for shows with episodes and movies (as opposed to, for example, news shows), so I set it to false iff the programID starts with SH, rather than if it's a TV show. (Note that the stik atom wasn't even being read by mutagen/mp4.py, and it's an integer.)
  3. If it's a Movie, then I think it's better to set movieYear than originalAirDate with the date.
  4. In order to read several of these mp4 atoms, I had to add the 8int and 32int parse/render routines into mutagen/mp4.py and the char routines in mutagen/_util.py. These are from updates to that project, but I only copied over the necessary routines.
  5. The last change may be somewhat controversial to you, but it would make the MP4 TiVo metadata complete. The idea is for those TiVo terms that don't map to any MP4 atom, just add a proprietary atom named ----:com.pyTivo.pyTivo:tiVoINFO (in parallel to ---:com.apple.iTunes:iTunMOVI) which would simply contain the JSON version of other pyTiVo attributes. This would then remove the need for the user to deal with the little .TXT files entirely, so that the metadata would simply follow along in the MP4 wherever it went. No effect on any programs that don't use it, but it would allow other programs to be more pyTiVo compliant. You'll see it's only a couple lines of code in pyTiVo. I hope you like this idea.

Finally, while I was spending so much time in there, I noticed a couple of very minor things:

First, if the TXT metadata contains colorCode : 4, then metadata['colorCode'] is set to 4, rather than ["COLOR",4], which breaks while generating the TVBus template. You'll see in the first commits, I wasn't sure how you'd prefer to fix it, and first did it by trying to maintain the array format, but finally decided that it was better to follow mpaaRating et al, and only keep the integer until the output in TVBus. I note in TVBus.tmpl, that there could be the same problem with showType, but I don't know what the right values are for that.

Secondly, the wiki says that the TXT format is Key : Value, but the dump routine wasn't providing the space before the colon.

wmcbrine commented 8 years ago

I don't want the added spaces. Otherwise, accepted.

mackworth commented 8 years ago

Great! If you'd like to try it out, cTiVo 2.4.1 beta4 has the MP4 code in it.

For your interest, the relevant code to generate the pyTivo-specific section is:

    NSMutableDictionary * tiVoInfo = [NSMutableDictionary dictionary];
    if (self.channelString.length >0) {
        [tiVoInfo setObject:self.channelString forKey:@"displayMajorNumber"];
    }
    if (self.showTime.length >0){
        [tiVoInfo setObject:self.showTime forKey:@"time"];
    }
    if (self.colorCode.length >0){
        [tiVoInfo setObject:self.colorCode forKey:@"colorCode"];
    }
    if (self.showingBits.length >0){
        [tiVoInfo setObject:self.showingBits forKey:@"showingBits"];
    }
    if (self.starRating.length >0){
        [tiVoInfo setObject:self.starRating forKey:@"starRating"];
    }
    if (self.startTime.length >0){
        [tiVoInfo setObject:self.startTime forKey:@"startTime"];
    }
    if (self.stopTime.length >0){
        [tiVoInfo setObject:self.stopTime forKey:@"stopTime"];
    }
    if (self.programId.length >0){
        [tiVoInfo setObject:self.programId forKey:@"programId"];
    }
    if (self.seriesId.length >0){
        [tiVoInfo setObject:self.seriesId forKey:@"seriesId"];
    }
    if (tiVoInfo.count) {
        NSData *serializedPlist = [NSPropertyListSerialization
                                   dataFromPropertyList:tiVoInfo
                                   format:NSPropertyListXMLFormat_v1_0
                                   errorDescription:nil];
        MP4ItmfItem* newItem = MP4ItmfItemAlloc( "----", 1 );
        newItem->mean = strdup( "com.pyTivo.pyTivo" );
        newItem->name = strdup( "tiVoINFO" );

        MP4ItmfData* data = &newItem->dataList.elements[0];
        data->typeCode = MP4_ITMF_BT_UTF8;
        data->valueSize = (unsigned int) [serializedPlist length];
        data->value = (uint8_t*)malloc( data->valueSize );
        memcpy( data->value, [serializedPlist bytes], data->valueSize );

        MP4ItmfAddItem(fileHandle, newItem);
        MP4ItmfItemFree(newItem);
    }