Пример #1
0
def move(plPath, currentIndex, newIndex):
    if currentIndex==newIndex:
        cfg.logger.info("Indexes Are the Same")
        return


    correctStateCorruption(plPath)

    currentDir = getLocalSongs(plPath)


    with shelve.open(f"{plPath}/{cfg.metaDataName}", 'c',writeback=True) as metaData:

        idsLen = len(metaData["ids"])
        numDigits = getNumDigets(idsLen)


        if currentIndex>=idsLen:
            cfg.logger.error(f"No song has Index {currentIndex}, Largest Index is {idsLen-1}")
            return
        elif currentIndex<0:
            cfg.logger.error(f"No Song has a Negative Index")
            return

        #clamp newIndex
        if newIndex > idsLen-1:
            newIndex = idsLen-1
        elif newIndex < 0:
            newIndex = 0
        
        cfg.logger.info(f"Moving {currentDir[currentIndex]} to Index {newIndex}")
        
        #moves song to end of list
        tempName = relabel(metaData,cfg.logger.debug,plPath,currentDir[currentIndex],currentIndex,idsLen,numDigits)

        if currentIndex>newIndex:
            #shifts all songs from newIndex to currentIndex-1 by +1
            for i in reversed(range(newIndex,currentIndex)):
                
                oldName = currentDir[i]
                relabel(metaData,cfg.logger.debug,plPath,oldName,i,i+1,numDigits)
    
        
        else:
            #shifts all songs from currentIndex+1 to newIndex by -1
            for i in range(currentIndex+1,newIndex+1):
                oldName = currentDir[i]
                relabel(metaData,cfg.logger.debug,plPath,oldName,i,i-1,numDigits)
        
        #moves song back
        relabel(metaData,cfg.logger.debug,plPath,tempName,idsLen,newIndex,numDigits)
        del metaData['ids'][idsLen]
Пример #2
0
def appendNew(plPath):
    '''will append new songs in remote playlist to local playlist in order that they appear'''

    cfg.logger.info("Appending New Songs...")

    correctStateCorruption(plPath)

    with shelve.open(f"{plPath}/{cfg.metaDataName}", 'c',writeback=True) as metaData:

        idsLen = len(metaData["ids"])
        numDigits = getNumDigets(idsLen)

        remoteIds = getIDs(metaData['url'])

        for remoteId in remoteIds:
            if remoteId not in metaData['ids']:
                download(metaData,plPath,remoteId,len(metaData['ids']),numDigits)
Пример #3
0
def shuffle(plPath):
    '''randomizes playlist order'''
    cfg.logger.info("Shuffling Playlist")
    correctStateCorruption(plPath)

    with shelve.open(f"{plPath}/{cfg.metaDataName}", 'c',writeback=True) as metaData:

        plLen = len(metaData["ids"])
        ids = metaData["ids"]

        avalibleNums = [i for i in range(plLen)]
        newOrder = []
        for _ in range(plLen):
            oldIndex = avalibleNums.pop(randint(0,len(avalibleNums)-1))
            newOrder.append( (ids[oldIndex],oldIndex) )
    
    editPlaylist(plPath, newOrder)
Пример #4
0
def manualAdd(plPath, songPath, posistion):
    '''put song in posistion in the playlist'''

    if not os.path.exists(songPath):
        cfg.logger.error(f'{songPath} Does Not Exist')
        return

    correctStateCorruption(plPath)

    currentDir = getLocalSongs(plPath)

    with shelve.open(f"{plPath}/{cfg.metaDataName}", 'c',writeback=True) as metaData:

        idsLen = len(metaData["ids"])
        numDigits = getNumDigets(idsLen)

        #clamp posistion
        if posistion > idsLen:
            posistion = idsLen
        elif posistion < 0:
            posistion = 0
        
        cfg.logger.info(f"Adding {ntpath.basename(songPath)} to {ntpath.basename(plPath)} in Posistion {posistion}")

        #shifting elements
        for i in reversed(range(posistion, idsLen)):
            oldName = currentDir[i]

            newName = re.sub(cfg.filePrependRE, f"{createNumLabel(i+1,numDigits)}_" , oldName)

            with noInterrupt:
                rename(metaData,cfg.logger.debug,plPath,oldName,newName,i+1,metaData["ids"][i])

                metaData["ids"][i] = '' #wiped in case of crash, this blank entries can be removed restoring state


        newSongName = f"{createNumLabel(posistion,numDigits)}_" + ntpath.basename(songPath)

        with noInterrupt:
            os.rename(songPath,f'{plPath}/{newSongName}')

            if posistion >= len(metaData["ids"]):
                metaData["ids"].append(cfg.manualAddId)
            else:
                metaData["ids"][posistion] = cfg.manualAddId
Пример #5
0
def swap(plPath, index1, index2):
    '''moves song to provided posistion, shifting all below it down'''
    if index1 == index2:
        cfg.logger.info(f"Given Index are the Same")


    correctStateCorruption(plPath)

    currentDir = getLocalSongs(plPath)



    with shelve.open(f"{plPath}/{cfg.metaDataName}", 'c',writeback=True) as metaData:

        idsLen = len(metaData["ids"])
        numDigits = getNumDigets(idsLen)

        if index1>=idsLen or index2>=idsLen:
            cfg.logger.error(f"Given Index is Larger than Max {idsLen-1}")
            return
        elif index1<0 or index2<0:
            cfg.logger.error(f"Given Index is Negative")
            return

        cfg.logger.info(f"Swapping {currentDir[index1]} and {currentDir[index2]}")
        #shift index1 out of the way (to idsLen)

        oldName = currentDir[index1]
        tempName = relabel(metaData,cfg.logger.debug,plPath,oldName,index1,idsLen,numDigits)

        
        #move index2 to index1's old location

        oldName = currentDir[index2]
        relabel(metaData,cfg.logger.debug,plPath,oldName,index2,index1,numDigits)

        #move index1 (now =idsLen) to index2's old location

        oldName = tempName
        relabel(metaData,cfg.logger.debug,plPath,oldName,idsLen,index2,numDigits)

        del metaData["ids"][idsLen]
Пример #6
0
    def test_removedSongs(self):
        cfg.logger.info(
            f"Running {self.__class__.__name__}: {self._testMethodName}")
        name = 'RemovedSongs'

        songs = ['A', 'B', 'C', 'D', 'E']

        createFakePlaylist(name, songs)

        os.remove(f'{cfg.testPlPath}/{name}/0_A')
        os.remove(f'{cfg.testPlPath}/{name}/4_E')
        os.remove(f'{cfg.testPlPath}/{name}/2_C')

        correctStateCorruption(f'{cfg.testPlPath}/{name}')

        correct = [('1', '0_B'), ('3', '1_D')]

        result = getPlaylistData(name)

        shutil.rmtree(f'{cfg.testPlPath}/{name}')
        self.assertEqual(result, correct)
Пример #7
0
    def test_blankMetaData(self):
        cfg.logger.info(
            f"Running {self.__class__.__name__}: {self._testMethodName}")
        name = 'blankMetaData'

        songs = ['A', 'B', 'C', 'D']

        createFakePlaylist(name, songs)

        with shelve.open(f"{cfg.testPlPath}/{name}/{cfg.metaDataName}",
                         'c',
                         writeback=True) as metaData:
            metaData['ids'].insert(2, '')

        correctStateCorruption(f'{cfg.testPlPath}/{name}')

        correct = [('0', '0_A'), ('1', '1_B'), ('2', '2_C'), ('3', '3_D')]

        result = getPlaylistData(name)

        shutil.rmtree(f'{cfg.testPlPath}/{name}')
        self.assertEqual(result, correct)
Пример #8
0
def smartSync(plPath):
    '''
    Syncs to remote playlist however will Not delete local songs (will reorder). Songs not in remote (ie ones deleted) 
    will be after the song they are currently after in local
    Example 1
        Local order: A B C D 
        Remote order: A 1 B C 2

        Local becomes: A 1 B C D 2

        notice D was removed from remote but is still after C in Local
    
    Example 2
        Local order: A B C D 
        Remote order: A 1 C B 2

        Local becomes: A 1 C D B 2

        notice C and B where swapped and D was deleted from Remote
    
    see test_smartSyncNewOrder in tests.py for more examples
    '''
    cfg.logger.info("Smart Syncing...")
    correctStateCorruption(plPath)


    with shelve.open(f"{plPath}/{cfg.metaDataName}", 'c',writeback=True) as metaData:
        url = metaData["url"]
        localIds = metaData["ids"]

    remoteIds = getIDs(url)


    newOrder = smartSyncNewOrder(localIds,remoteIds)

    editPlaylist(plPath,newOrder)
Пример #9
0
def cli():
    '''
    Runs command line application, talking in args and running commands
    '''
    args = parseArgs()

    setupLogger(args)

    cwd = getCwd(args)

    #peek command runs without PLAYLIST posistional argument
    if args.peek:
        url = args.peek[0]
        if len(args.peek) < 2:
            peek(url)
            sys.exit()
        fmt = args.peek[1]

        peek(url, fmt)
        sys.exit()

    # if no playlist was provided all further functions cannot run
    if args.PLAYLIST:
        plPath = f"{cwd}/{args.PLAYLIST}"
    else:
        if not args.local_dir:  #only option which can run without playlist
            cfg.logger.error("Playlist Name Required")
        sys.exit()

    if args.new_playlist:
        newPlaylist(plPath, args.new_playlist)
        sys.exit()

    if not playlistExists(plPath):
        sys.exit()

    #viewing playlist
    if args.print:
        showPlaylist(plPath)

    if args.view_metadata:
        compareMetaData(plPath)

    #playlist managing
    try:
        #smart syncing
        if args.smart_sync:
            smartSync(plPath)

        #appending
        elif args.append_new:
            appendNew(plPath)

        #manual adding
        elif args.manual_add:
            if not args.manual_add[1].isdigit():
                cfg.logger.error("Index must be positive Integer")
            else:

                manualAdd(plPath, args.manual_add[0], int(args.manual_add[1]))

        #moving/swaping songs
        elif args.move:
            move(plPath, args.move[0], args.move[1])

        elif args.move_range:
            moveRange(plPath, args.move_range[0], args.move_range[1],
                      args.move_range[2])

        elif args.swap:
            swap(plPath, args.swap[0], args.swap[1])

        # TODO uncomment out --push-order once google completes oauth verification process
        #elif args.push_order:
        #    pushLocalOrder(plPath)

    #fixing metadata corruption in event of crash
    except Exception as e:
        cfg.logger.exception(e)
        correctStateCorruption(plPath)
        cfg.logger.info("State Recovered")

    except:  #sys.exit calls
        correctStateCorruption(plPath)
        cfg.logger.info("State Recovered")
Пример #10
0
def moveRange(plPath, start, end, newStart):
    '''
    moves block of songs from start to end indices, to newStart
    ie) start = 4, end = 6, newStart = 2
    0 1 2 3 4 5 6 7 -> 0 1 4 5 6 2 3
    '''

    correctStateCorruption(plPath)

    if start == newStart:
        return

    currentDir = getLocalSongs(plPath)

    with shelve.open(f"{plPath}/{cfg.metaDataName}", 'c',writeback=True) as metaData:

        idsLen = len(metaData["ids"])
        numDigits = getNumDigets(idsLen)


        if start>=idsLen:
            cfg.logger.error(f"No song has Index {start}, Largest Index is {idsLen-1}")
            return

        elif start<0:
            cfg.logger.error(f"No Song has a Negative Index")
            return
            
        #clamp end index
        if end>=idsLen or end == -1:
            end = idsLen-1

        elif end<=start:
            cfg.logger.error("End Index Must be Greater Than Start Index (or -1)")
            return

        #clamp newStart
        if newStart > idsLen:
            newStart = idsLen
        elif newStart < -1:
            newStart = -1
        
        # Sanatization over

        # number of elements to move
        blockSize = end-start+1

        # make room for block
        for i in reversed(range(newStart+1,idsLen)):
            oldName = currentDir[i]
            newIndex =i+blockSize
            relabel(metaData,cfg.logger.debug,plPath,oldName,i,newIndex,numDigits)
        

        #accounts for block of songs being shifted if start>newStart
        offset = 0
        if start>newStart:
            currentDir = getLocalSongs(plPath)
            offset = blockSize
        
        # shift block into gap made
        for i,oldIndex in enumerate(range(start,end+1)):

            oldName = currentDir[oldIndex]
            newIndex = i + newStart+1
            relabel(metaData,cfg.logger.debug,plPath,oldName,oldIndex+offset,newIndex,numDigits)

    # remove number gap in playlist and remove blanks in metadata
    correctStateCorruption(plPath)