نفوذ به دنیای موسیقی
آموزش کار با زبان برنامه‌نويسی پايتون (بخش سیزدهم)
شايد شما نیز بسیاری از موسیقی‌های مورد علاقه‌تان را در قالب فايل‌های mp3 روی هارد‌دیسک کامپیوترتان ذخیره کرده‌اید. تا زمانی که تعداد این فایل‌ها به هزار نرسیده است، مدیریت و به خاطر سپردن محل آن‌ها ساده خواهد بود. اما اگر تعداد فایل‌هایتان بیشتر باشد، کار بسیار مشکل خواهد شد. چند سال پیش‌تر، بحث اصلی بر سر فضای ذخیره‌سازی بود، اما اکنون مشکل اصلی این است که به یاد بیاورید کدام موسیقی در چه فایلی و با چه نامی در کجا ذخیره شده است. در این شماره سعی خواهیم کرد، کاتالوگی برای آرشیو موسیقی‌مان به‌وجود آوریم و در این راه از مهارت‌هایی که در کار با پایگاه‌های داده کسب کردیم، استفاده خواهیم کرد و با مفاهیم تازه‌ای نیز آشنا خواهیم شد.
 

این مقاله یکی از قسمت‌های سلسله مقالاتی برای آشنایی و آموزش زبان پایتون است. این مجموع پیش از این در ماهنامه شبکه منتشر شده اما به سایت جدید منتقل نشده بود. با توجه به اهمیت موضوع و درخواست‌های مکرر خوانندگان، این مجموعه را به سایت مجله اضافه می‌کنیم و امیدواریم که مورد توجه علاقمندان قرار بگیرد.

 

برای مطالعه قسمت‌های قبلی سلسله مقالات آموزش زبان برنامه‌نویسی پایتون اینجا کلیک کنید.


متادیتا

نخستين نکته‌ای که باید بدانید این است که فایل‌های mp3 می‌توانند اطلاعاتی را درباره خود فایل ذخیره کنند. اطلاعاتی نظیر عنوان آهنگ، عنوان آلبوم و نام خواننده از جمله این اطلاعات هستند. این اطلاعات در برچسب‌های ID3 ذخیره می‌شود که به اصطلاح فرا داده يا «متادیتا» نامیده می‌شوند. در نخستین روزهای ظهور فایل‌های mp3 تنها مقدار بسیار کمی از اين اطلاعات در فایل‌های mp3 ذخیره می‌شد. در آن زمان این اطلاعات در قسمت پايانی فایل‌های mp3 ذخیره می‌شد و بلوکی متشکل از 128 بایت بود. به‌واسطه حجم اندک این بلوک، تنها 30 کاراکتر به هر یک از اطلاعاتی نظیر نام آهنگ و آلبوم و خواننده اختصاص داده شده بود. برای بسیاری از فایل‌ها همین مقدار کافی بود، اما اگر با آهنگی با نام طولانی، نام آلبومی طولانی و... روبه‌رو می‌شدیم، مشکل خودنمایی می‌کرد. این برچسب‌های ID3 تحت‌عنوان استاندارد ID3v1 طبقه‌بندی شدند و برای تکمیل آن نسخه جدیدی از این استاندارد با نام ID3v2 معرفی شد. این استاندارد جدید امکان ذخیره اطلاعاتی با طول‌های متغیر را آن هم در ابتدای فایل فراهم می‌آورد. با استفاده از این استاندارد، اطلاعات ID3v1 هنوز برای حفظ سازگاری با پخش‌کننده‌های قدیمی در انتهای فایل حفظ می‌شدند. به کمک این استاندارد جدید، متادیتا می‌توانست حجمی تا حداکثر 256 مگابایت داشته باشد! در این استاندارد هر گروه از اطلاعات در یک چهارچوب یا فریم (Frame) نگه‌داری می‌شود و هر فریم یک معرف یا Identifier دارد. در نسخه فعلی (2/4) این استاندارد، طول این معرف چهار کاراکتر است.

import os , sys
from os.path import join , getsize , exists
from mutagen.mp3 import MP3
import apsw
def MakeDatabase():
pass
def S2HMS(t):
pass
def WalkThePath(musicpath):
pass
def error(message):
pass
def main():
pass
def usage():
pass
if __name__ == ‘__main__’:
main()

فهرست 1-  چهارچوب اصلی برنامه

موتاژن 

پیش‌تر برای کار با این متادیتاها، باید فایل را به‌صورت باینری باز‌می‌کردیم و در آن‌ اطلاعات موردنظر را جست‌وجو مي‌کرديم. این کار اگرچه ممکن و ساده بود، اما به زمان و کدنویسی بسیاری نیاز داشت. اما اکنون دیگر می‌توانیم از کتابخانه‌های متعددی که برای این کار تهیه شده‌اند، استفاده کنيم. ما از کتابخانه‌ای به نام موتاژن (Mutagen) در این پروژه استفاده خواهیم کرد. برای ادامه کار در اوبونتو در مدیر بسته Synaptic موتاژن را جست‌وجو کرده و آن را نصب کنید. اگر از سیستم‌های ویندوزی برای کدنویسی استفاده می‌کنید، می‌توانید این کتابخانه را از آدرس http://code.google.com/p/mutage دریافت و نصب‌کنید. البته، موتاژن به غیر از فایل‌های mp3 قابلیت کار با فرمت‌های M4A ،Ogg Vorbis ، ASF و بسیاری فرمت‌های دیگر را هم دارد. در این پروژه علاوه بر موتاژن مانند دو قسمت قبل به کتابخانه apsw نیز برای کار با پایگاه‌های داده نیاز خواهیم داشت.

def usage():
message = (
‘====================================\n’
‘mCat finds all mp3 files in a folder\n’
‘and subfolders. Reads the ID3 tags and \n’
‘writes the information to a SQLite database.\n’
‘usage:\n’
‘\t %s <foldername>\n’
‘\t WHERE <foldername> is the path to the mp3 files.\n’
‘====================================\n’
) %sys.argv[0]
print type(sys.argv)
error(message)
sys.exit(1)

فهرست 2-  کدهای مورد نياز برای تکميل تابع Usage

شروع

فایل جدیدی با نام mCat.py‌ ایجاد کنید و کدهای فهرست 1 را که چهارچوب اصلی برنامه ما را می‌سازند، در آن وارد کنید. اگرچه این فایل در این وضعیت هیچ کاری انجام نمی‌دهد، اما با اجرای آن و در صورت عدم دریافت پیغام خطا می‌توانید مطمئن شوید که تمام کتابخانه‌های مورد نیاز را به درستی نصب‌کرده‌اید. 
در اين فهرست ما اسکلت مربوط به توابع مورد نياز را پياده کرده‌ايم. تنها نکته عجيب شايد آخرين قسمت اين کد يعنی خطوط 17 و18 باشد. هنگامي که يک کد به اجرا در می‌آيد، چه به صورت مستقيم و چه با import شدن در ساير کدها، پايتون نامی را به آن نسبت می‌دهد و با آن همانند يک شیء رفتار می‌کند. زمانی که کد به تنهايی اجرا شود، نامی که به آن نسبت داده می‌شود __main__ است. با دستور if موجود در خط 17 ما کنترل می‌کنيم که آيا اين فايل به‌صورت مستقيم اجرا شده است يا خير؟ اگر فايل به صورت مستقيم اجرا شده بود، تابع اصلی برنامه يعنی ()main اجرا خواهد شد. اما اگر فايل در يک کد ديگر import شده باشد، هيچ کدی به‌صورت مستقيم اجرا نخواهد شد و برنامه منتظر می‌ماند تا توابع موجود در آن توسط برنامه import کننده فراخوانده شود.

def main():
global connection
global cursor
if len(sys.argv) != 2:
usage()
else:
StartFolder = sys.argv[1]
if not exists(StartFolder):
print “Folder %s does not exist. Exiting.” %StartFolder
sys.exit(1)
else:
print “\nWorking on %s” %StartFolder
connection = apsw.Connection(“mCat.db3”)
cursor = connection.cursor()
MakeDataBase()
WalkThePath(StartFolder)
cursor.close()
connection.close()
print “\nFinished.”

فهرست 3-  کدهای مورد نياز برای تکميل تابع main

def MakeDataBase():
    sql = ‘CREATE TABLE IF NOT EXISTS mp3 (pkID INTEGER PRIMARY KEY,’
    sql = sql + ‘ title TEXT, artist TEXT, album TEXT, bitrate TEXT, ‘
    sql = sql + ‘genre TEXT, playtime TEXT, track INTEGER, year TEXT,’
    sql = sql + ‘ filesize TEXT, path TEXT, filename TEXT);’
    cursor.execute(sql)

فهرست 4-  کدهای مورد نياز برای تکميل تابع (Make Data Base(1

تعريف توابع

حال به تکميل اين توابع مي‌پردازيم. در ابتدا به سراغ تابع ()usage مي‌رويم. اين تابع نحوه کار برنامه ما را براي کاربر توضيح خواهد داد. همچنين با اعلام بروز خطا اجراي برنامه را متوقف خواهد کرد. کدهای اين تابع را در فهرست 2 مشاهده می‌کنيد.
در اين کدها که بايد جايگزين خطوط 15 و‌16 فايل اصلی (فهرست 1) شوند، ابتدا رشته‌ای با نام message‌ در خطوط 2 تا 11 تعريف شده است. دو نکته کوچک در تعريف اين متغير وجود دارد. نخست اين‌که اگر تعدادی رشته را بدون هيچ عملگری پشت سر‌هم رديف کنيم، پايتون آن‌ها را به ترتيب به هم افزوده و يک رشته طولانی‌تر به‌وجود خواهد آورد؛ دوم اين‌که در خط 8 ما با s% جايی را برای درج يک رشته خالی گذاشته‌ايم. اين جای خالی در خط 12 و از طريق [sys.argv[0 % با نام فايل حاوی برنامه پر شده است. با import کردن ماجول sys ما به متغيری به نام argv دسترسی خواهيم داشت. اين متغير که از جنس ليست است، حاوی کل آرگومان‌هايی است که در هنگام اجرای برنامه از طريق خط فرمان وارد‌شده‌اند. برای نمونه، اگر برای اجرای فايل mCat در خط فرمان از دستور زير استفاده شود:

python mCat.py /usr/Music Ali 1234

 مقدار [‘mCat.py’ , ‘/usr/Music’ , ‘Ali’ , ‘1234’]  در متغير argv ذخيره خواهد داشت. در اين متغير آرگومان اول يا [argv[0همواره نام فايل اجرا شده خواهد بود. 
در خط 13 ما تابع error را برای اطلاع‌دادن به کاربر فراخوانده‌ايم و پس از آن در خط 14 با استفاده از (sys.exit(1 از برنامه خارج شده‌ايم. تابع exit از ماجول sys براي پايان بخشيدن به اجرای برنامه‌ها به کار می‌رود و در صورتی که مقدار ارسال شده به آن 0 باشد، به سيستم اعلام می‌کند که اجرا با موفقيت به پايان رسيده است. در غير اين صورت مشخص خواهد شد که اجرای برنامه به‌واسطه خطا متوقف شده است.
حال نوبت به تعريف تابع error می‌رسد. برای تکميل اين تابع عبارت زير را جايگزين کلمه pass در خط 12 فهرست 1 کنيد.

print >> sys.stderr , message

در اين دستور print، ما از قابليت تغيير مسير خروجی‌ استفاده‌ کرده‌ايم و پيغام را به خروجی استاندارد تعريف شده براي پيغام‌های خطا هدايت‌ کرده‌ايم. توضيح ضروری اين‌که در سيستم‌عامل به‌صورت استاندارد، خروجي‌هايی براي چاپ اطلاعات معمول، اطلاعات مربوط به خطاها و ورودی اطلاعات کاربر در نظر گرفته مي‌شود که به ترتيب stdout ، stderr و stdin ناميده می‌شوند. زمانی که شما از دستور print به‌صورت عادی استفاده مي‌کنيد، اطلاعات شما به‌صورت خودکار به stdout منتقل‌می‌شود که در پايتون اين خروجی روی ترمينال‌تعريف شده است. خروجی stderr نيز به‌صورت پيش‌فرض روی همان ترمينال است، اما می‌توان آن را به محل‌های ديگری (مثلاً يک فايل متنی برای ذخيره کل پيغام‌های خطا) نيز هدايت کرد. از اين خروجی‌ها و ورودی‌های متفاوت می‌توان برای تهيه logهای دقيق و کامل از اجرای نرم‌افزار استفاده کرد. 
اکنون بايد به سراغ تابع اصلی يا main برويم. اين تابع که کدهای آن را در فهرست 3 مشاهده می‌کنيد، ابتدا اتصال يا connection و مکان‌نما يا cursor مورد نياز را برای کار با پايگاه داده تعريف کرده و پس از آن با کنترل آرگومان‌هايی که کاربر هنگام اجرای برنامه وارد کرده و در صورتی که آرگومان‌ها درست باشند،‌ اعمال اصلی برنامه را به انجام می‌رساند.
در اينجا همانند دو شماره پيشين، متغيرهای عمومی connection و cursor‌ را برای کار با پايگاه داده تعريف کرده‌ايم. پس از آن پارامترهايی را که کاربر در خط فرمان وارد کرده است، کنترل کرده‌ايم. ما ورودی کاربر را برای يافتن 2 پارامتر کنترل می‌کنيم که نخستين مورد، نام برنامه اجرا‌شده و دومی آدرس پوشه موردنظر است. به خاطر داشته باشيد که اگر آدرس شما حاوی کاراکتر فضای خالی (space) باشد، به عنوان دو پارامتر جدا در نظر گرفته شده و باعث بروز خطا می‌شود. در اين حالت بايد آدرس را در علامت‌های کوتيشن قرار دهيد. اگر کاربر آدرس پوشه‌ای را وارد نکرده يا بيش از يک آدرس را وارد کرده باشد، با پيغام خطا روبه‌رو خواهد شد. اگر ورودی کاربر درست باشد، ابتدا در خط 9 کنترل می‌کنيم که آيا چنين پوشه‌ای موجود است يا خير. تابع exists() که از ماجول os آورده شده است، با گرفتن يک آدرس در سيستم فايلی کامپيوتر وجود يا عدم‌وجود آن را کنترل می‌کند.
پس از آن و در خط 16 تابع ساخت پايگاه‌داده (MakeDataBase) فراخوانده شده است. اين تابع که در فهرست 4 آورده شده، درصورت عدم وجود جدول mp3  در فايلmCat.db3 آن را بامشخصات مورد نظر ايجاد مي‌کند. پس از آن با فراخواني تابع ()WalkThePath کل پوشه‌ داده شده بررسي و فايل‌هاي mp3 آن شناسايی شده و اطلاعات به پايگاه داده منتقل می‌شود. پس از آن هر دو شیء connection و cursor بسته شده‌اند. 
در این تابع ما ابتدا سه شمارنده برای ردگیری تعداد خطاها، پوشه‌ها و فایل‌ها ایجاد کرده‌ایم. پس از آن برای نگه‌داری سوابق خطاهای احتمالی، فایلی با نام errors.log ایجاد شده است. پس از آن به کمک تابعی که ماجول os در اختیار ما قرار می‌دهد، به بررسی پوشه‌ مورد نظر پرداخته‌ایم. سیستم کار تابع ()walk به این ترتيب است که کار را از پوشه‌ داده شده توسط کاربر شروع کرده و به تمام پوشه‌ها و زیرپوشه‌های موجود در آن به ترتیب وارد شده و این روند را برای هر یک از زیرپوشه‌ها نیز تکرار می‌کند. در هرکدام از زیرپوشه‌ها، ما به دنبال فایل‌هایی گشته‌ایم که پسوندشان mp3 باشد. پس از آن به سراغ هریک از فایل‌های یافت شده رفته‌ایم و ابتدا متغیرهای محلی مربوط به آن را از محتوای قبلی پاک کرده‌ایم. پس از آن از تابع ()join ماجول os.path استفاده کرده‌ایم و آدرس کامل فایل را برای موتاژن آماده کرده وآن را به عنوان آرگومان تابع ()MP3 موتاژن مورد استفاده قرار می‌دهیم تا وهله‌ای از شیء audio را برای ما ایجاد کند. پس از آن کل برچسب‌های ID3 آن را خوانده و برچسب‌های موردنظرمان را جدا کرده و در متغیرهای موقتی ذخیره می‌کنیم. آن‌گاه از این متغیرها برای ساختن دستور SQL و وارد کردن اطلاعات در پایگاه داده استفاده می‌کنیم. تنها نکته قابل توجه شاید فرم دستور SQL باشد. در اینجا نیز ما با فرمتی شبیه %s در دستور print، محل مقادیر را با ؟ مشخص کرده‌ایم و بعدتر هنگام اجرای دستور SQL این متغیرها را در محل موردنظر جایگذاری می‌کنیم. 
در انتها در قسمت کنترل خطاها با نوع دیگری از قالب‌بندی رشته‌ها و جایگذاری مقادیر مواجه می‌شوید که شاید کمی عجیب باشد. این syntax برنامه‌نویسی یکی از اجزای اجباری سری 3.x پایتون است. در این شیوه مقادیر {0} و {1} . . . با مقادیر متناظر در پرانتز format جایگزین می‌شوند.
کدهای مربوط به تابع MakeDataBase را در فهرست 4 مشاهده می‌کنيد. در اينجا با توجه به نکته‌هايی که در دو شماره قبل درباره ايجاد پايگاه‌های‌داده‌گفتيم، نبايد در درک روند انجام کار این تابع مشکلی وجود داشته باشد. در اين مورد هم به سادگی يک دستور SQL برای ساخت يک جدول با فيلدهايی نظير نام آلبوم، نام هنرمند، ژانر موسيقی و... ساخته و اجرا شده است.
 نکته مهمی که بايد به آن توجه کنيد اين است که شما باید پيش از اجرای برنامه، فايل پايگاه داده را به‌شخصه ساخته و در پوشه‌ برنامه قرار دهيد. کدهاي اين فهرست را جايگزين خطوط 5 و 6 فهرست 1 کنيد. البته می‌توانید به دلخواه خود کدهایی را به اين تابع و تابع ()main   بیافزایید تا آدرس محل ذخیره پایگاه داده یا نام آن نیز از کاربر پرسیده شود و فایل موردنظر نیز درست همزمان با اجرای برنامه ایجاد شود.

def S2HMS(t):
    if t > 3600:
        h = int(t/3600)
        r = t-(h*3600)
        m = int(r / 60)
        s = int(r-(m*60))
        return ‘%d:%02d:%02d’.format(h,m,s)
    else:
        m = int(t / 60)
        s = int(t-(m*60))
        return ‘%d:%02d’.format(m,s)

فهرست 5-  کدهای مورد نياز برای تکميل تابع S2HMS

در نهايت، به بررسی تابع S2HMS می‌پردازيم. این تابع که کدهای آن را در فهرست 5 مشاهده می‌کنید، مدت هر آهنگ را که توسط موتاژن به صورت یک عدد اعشاری برگردانده می‌شود به فرمت ساعت:دقیقه:‌ثانیه تبدیل می‌کند. به نحوه مرتب کردن خروجی تابع در دستورات return در خطوط 7 و‌11 توجه کنید. با عبارت %02dما از پایتون می‌خواهیم که عدد جايگزين را حتی اگر تک رقمی بود، دو رقمی منظور کند و به جای رقم نخست آن 0 قرار دهد.

استفاده

با تکمیل آخرین تابع دیگر زمان استفاده از برنامه فرا رسیده است. حال می‌توانید با دستور زیر برنامه را اجرا کنید: 

python mCat.py <folder>

فقط آدرس پوشه‌ محل ذخیره‌سازی موسیقی‌هایتان را جایگزین قسمت <folder> کنید. در این صورت پایگاه داده‌ای حاوی اطلاعات تمام فایل‌های موسیقی‌ در اختیار خواهید داشت. با اندکی صبر و حوصله و در صورت نیاز مراجعه به قسمت‌های قبلی، می‌توانید به سادگی کدی بنویسید که بتواند آهنگ یا آلبوم مورد نظر شما را در این پایگاه داده جست‌وجو کرده و محل ذخیره و سایر اطلاعات آن را برای شما به نمایش در‌آورد.

ماهنامه شبکه را از کجا تهیه کنیم؟
ماهنامه شبکه را می‌توانید از کتابخانه‌های عمومی سراسر کشور و نیز از دکه‌های روزنامه‌فروشی تهیه نمائید.

ثبت اشتراک نسخه کاغذی ماهنامه شبکه     
ثبت اشتراک نسخه آنلاین

 

کتاب الکترونیک +Network راهنمای شبکه‌ها

  • برای دانلود تنها کتاب کامل ترجمه فارسی +Network  اینجا  کلیک کنید.

کتاب الکترونیک دوره مقدماتی آموزش پایتون

  • اگر قصد یادگیری برنامه‌نویسی را دارید ولی هیچ پیش‌زمینه‌ای ندارید اینجا کلیک کنید.

ایسوس

نظر شما چیست؟